5220 lines
180 KiB
C
5220 lines
180 KiB
C
/*++
|
|
Copyright (c) 1997 Microsoft Corporation
|
|
|
|
Module Name:
|
|
mountmgr.c
|
|
|
|
Abstract:
|
|
This driver manages the kernel mode mount table that handles the level of indirection between the persistent dos device name for an object and the non-persistent nt device name for an object.
|
|
|
|
Author:
|
|
Norbert Kusters 20-May-1997
|
|
|
|
Environment:
|
|
kernel mode only
|
|
--*/
|
|
|
|
#define _NTDDK_
|
|
#define _NTSRV_
|
|
|
|
#include <ntos.h>
|
|
#include <zwapi.h>
|
|
#include <ntdddisk.h>
|
|
#include <ntddvol.h>
|
|
#include <initguid.h>
|
|
#include <wdmguid.h>
|
|
#include <mountmgr.h>
|
|
#include <mountdev.h>
|
|
#include <mntmgr.h>
|
|
#include <stdio.h>
|
|
#include <ioevent.h>
|
|
|
|
#define MAX_VOLUME_PATH 100
|
|
|
|
#define IOCTL_MOUNTMGR_QUERY_POINTS_ADMIN CTL_CODE(MOUNTMGRCONTROLTYPE, 2, METHOD_BUFFERED, FILE_READ_ACCESS)
|
|
|
|
NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath);
|
|
NTSTATUS UniqueIdChangeNotifyCompletion(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID WorkItem);
|
|
NTSTATUS MountMgrChangeNotify(IN OUT PDEVICE_EXTENSION Extension, IN OUT PIRP Irp);
|
|
VOID MountMgrNotify(IN PDEVICE_EXTENSION Extension);
|
|
VOID ReconcileThisDatabaseWithMaster(IN PDEVICE_EXTENSION Extension, IN PMOUNTED_DEVICE_INFORMATION DeviceInfo);
|
|
NTSTATUS QueueWorkItem(IN PDEVICE_EXTENSION Extension, IN PWORK_QUEUE_ITEM WorkItem);
|
|
NTSTATUS MountMgrMountedDeviceRemoval(IN PDEVICE_EXTENSION Extension, IN PUNICODE_STRING NotificationName);
|
|
|
|
typedef struct _RECONCILE_WORK_ITEM_INFO {
|
|
PDEVICE_EXTENSION Extension;
|
|
PMOUNTED_DEVICE_INFORMATION DeviceInfo;
|
|
} RECONCILE_WORK_ITEM_INFO, *PRECONCILE_WORK_ITEM_INFO;
|
|
|
|
typedef struct _RECONCILE_WORK_ITEM {
|
|
WORK_QUEUE_ITEM WorkItem;
|
|
RECONCILE_WORK_ITEM_INFO WorkItemInfo;
|
|
} RECONCILE_WORK_ITEM, *PRECONCILE_WORK_ITEM;
|
|
|
|
#ifdef POOL_TAGGING
|
|
#undef ExAllocatePool
|
|
#define ExAllocatePool(a,b) ExAllocatePoolWithTag(a,b,' tnM')
|
|
#endif
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(INIT, DriverEntry)
|
|
#endif
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma code_seg("PAGE")
|
|
#endif
|
|
|
|
|
|
NTSTATUS
|
|
QueryDeviceInformation(
|
|
IN PUNICODE_STRING NotificationName,
|
|
OUT PUNICODE_STRING DeviceName,
|
|
OUT PMOUNTDEV_UNIQUE_ID* UniqueId,
|
|
OUT PBOOLEAN IsRemovable,
|
|
OUT PBOOLEAN IsRecognized
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
This routine queries device information.
|
|
Arguments:
|
|
NotificationName - Supplies the notification name.
|
|
DeviceName - Returns the device name.
|
|
UniqueId - Returns the unique id.
|
|
IsRemovable - Returns whether or not the device is removable.
|
|
IsRecognized - Returns whether or not this is a recognized partition type.
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
PFILE_OBJECT fileObject;
|
|
PDEVICE_OBJECT deviceObject;
|
|
BOOLEAN isRemovable;
|
|
PARTITION_INFORMATION partInfo;
|
|
KEVENT event;
|
|
PIRP irp;
|
|
IO_STATUS_BLOCK ioStatus;
|
|
ULONG outputSize;
|
|
PMOUNTDEV_NAME output;
|
|
PIO_STACK_LOCATION irpSp;
|
|
|
|
status = IoGetDeviceObjectPointer(NotificationName, FILE_READ_ATTRIBUTES,
|
|
&fileObject, &deviceObject);
|
|
if (!NT_SUCCESS(status)) {
|
|
return status;
|
|
}
|
|
if (fileObject->FileName.Length) {
|
|
ObDereferenceObject(fileObject);
|
|
return STATUS_OBJECT_NAME_NOT_FOUND;
|
|
}
|
|
|
|
if (fileObject->DeviceObject->Characteristics&FILE_REMOVABLE_MEDIA) {
|
|
isRemovable = TRUE;
|
|
} else {
|
|
isRemovable = FALSE;
|
|
}
|
|
|
|
if (IsRemovable) {
|
|
*IsRemovable = isRemovable;
|
|
}
|
|
|
|
deviceObject = IoGetAttachedDeviceReference(fileObject->DeviceObject);
|
|
|
|
if (IsRecognized) {
|
|
if (isRemovable) {
|
|
*IsRecognized = TRUE;
|
|
} else {
|
|
KeInitializeEvent(&event, NotificationEvent, FALSE);
|
|
irp = IoBuildDeviceIoControlRequest(IOCTL_DISK_GET_PARTITION_INFO, deviceObject, NULL, 0, &partInfo, sizeof(partInfo), FALSE, &event, &ioStatus);
|
|
if (!irp) {
|
|
ObDereferenceObject(deviceObject);
|
|
ObDereferenceObject(fileObject);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
status = IoCallDriver(deviceObject, irp);
|
|
if (status == STATUS_PENDING) {
|
|
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
|
|
status = ioStatus.Status;
|
|
}
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
status = STATUS_SUCCESS;
|
|
*IsRecognized = TRUE;
|
|
} else if (IsRecognizedPartition(partInfo.PartitionType)) {
|
|
*IsRecognized = TRUE;
|
|
} else {
|
|
*IsRecognized = FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (DeviceName) {
|
|
outputSize = sizeof(MOUNTDEV_NAME);
|
|
output = ExAllocatePool(PagedPool, outputSize);
|
|
if (!output) {
|
|
ObDereferenceObject(deviceObject);
|
|
ObDereferenceObject(fileObject);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
KeInitializeEvent(&event, NotificationEvent, FALSE);
|
|
irp = IoBuildDeviceIoControlRequest(IOCTL_MOUNTDEV_QUERY_DEVICE_NAME, deviceObject, NULL, 0, output, outputSize, FALSE, &event, &ioStatus);
|
|
if (!irp) {
|
|
ExFreePool(output);
|
|
ObDereferenceObject(deviceObject);
|
|
ObDereferenceObject(fileObject);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
irpSp = IoGetNextIrpStackLocation(irp);
|
|
irpSp->FileObject = fileObject;
|
|
|
|
status = IoCallDriver(deviceObject, irp);
|
|
if (status == STATUS_PENDING) {
|
|
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
|
|
status = ioStatus.Status;
|
|
}
|
|
|
|
if (status == STATUS_BUFFER_OVERFLOW) {
|
|
outputSize = sizeof(MOUNTDEV_NAME) + output->NameLength;
|
|
ExFreePool(output);
|
|
output = ExAllocatePool(PagedPool, outputSize);
|
|
if (!output) {
|
|
ObDereferenceObject(deviceObject);
|
|
ObDereferenceObject(fileObject);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
KeInitializeEvent(&event, NotificationEvent, FALSE);
|
|
irp = IoBuildDeviceIoControlRequest(IOCTL_MOUNTDEV_QUERY_DEVICE_NAME, deviceObject, NULL, 0, output, outputSize, FALSE, &event, &ioStatus);
|
|
if (!irp) {
|
|
ExFreePool(output);
|
|
ObDereferenceObject(deviceObject);
|
|
ObDereferenceObject(fileObject);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
irpSp = IoGetNextIrpStackLocation(irp);
|
|
irpSp->FileObject = fileObject;
|
|
|
|
status = IoCallDriver(deviceObject, irp);
|
|
if (status == STATUS_PENDING) {
|
|
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
|
|
status = ioStatus.Status;
|
|
}
|
|
}
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
DeviceName->Length = output->NameLength;
|
|
DeviceName->MaximumLength = output->NameLength + sizeof(WCHAR);
|
|
DeviceName->Buffer = ExAllocatePool(PagedPool, DeviceName->MaximumLength);
|
|
if (DeviceName->Buffer) {
|
|
RtlCopyMemory(DeviceName->Buffer, output->Name, output->NameLength);
|
|
DeviceName->Buffer[DeviceName->Length/sizeof(WCHAR)] = 0;
|
|
} else {
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
}
|
|
|
|
ExFreePool(output);
|
|
}
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
ObDereferenceObject(deviceObject);
|
|
ObDereferenceObject(fileObject);
|
|
return status;
|
|
}
|
|
|
|
if (UniqueId) {
|
|
outputSize = sizeof(MOUNTDEV_UNIQUE_ID);
|
|
output = ExAllocatePool(PagedPool, outputSize);
|
|
if (!output) {
|
|
ObDereferenceObject(deviceObject);
|
|
ObDereferenceObject(fileObject);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
KeInitializeEvent(&event, NotificationEvent, FALSE);
|
|
irp = IoBuildDeviceIoControlRequest(IOCTL_MOUNTDEV_QUERY_UNIQUE_ID, deviceObject, NULL, 0, output, outputSize, FALSE, &event, &ioStatus);
|
|
if (!irp) {
|
|
ExFreePool(output);
|
|
ObDereferenceObject(deviceObject);
|
|
ObDereferenceObject(fileObject);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
irpSp = IoGetNextIrpStackLocation(irp);
|
|
irpSp->FileObject = fileObject;
|
|
|
|
status = IoCallDriver(deviceObject, irp);
|
|
if (status == STATUS_PENDING) {
|
|
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
|
|
status = ioStatus.Status;
|
|
}
|
|
|
|
if (status == STATUS_BUFFER_OVERFLOW) {
|
|
outputSize = sizeof(MOUNTDEV_UNIQUE_ID) + ((PMOUNTDEV_UNIQUE_ID) output)->UniqueIdLength;
|
|
ExFreePool(output);
|
|
output = ExAllocatePool(PagedPool, outputSize);
|
|
if (!output) {
|
|
ObDereferenceObject(deviceObject);
|
|
ObDereferenceObject(fileObject);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
KeInitializeEvent(&event, NotificationEvent, FALSE);
|
|
irp = IoBuildDeviceIoControlRequest(IOCTL_MOUNTDEV_QUERY_UNIQUE_ID, deviceObject, NULL, 0, output, outputSize, FALSE, &event, &ioStatus);
|
|
if (!irp) {
|
|
ExFreePool(output);
|
|
ObDereferenceObject(deviceObject);
|
|
ObDereferenceObject(fileObject);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
irpSp = IoGetNextIrpStackLocation(irp);
|
|
irpSp->FileObject = fileObject;
|
|
|
|
status = IoCallDriver(deviceObject, irp);
|
|
if (status == STATUS_PENDING) {
|
|
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
|
|
status = ioStatus.Status;
|
|
}
|
|
}
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
*UniqueId = (PMOUNTDEV_UNIQUE_ID) output;
|
|
} else {
|
|
ExFreePool(output);
|
|
}
|
|
}
|
|
|
|
ObDereferenceObject(deviceObject);
|
|
ObDereferenceObject(fileObject);
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
NTSTATUS FindDeviceInfo(
|
|
IN PDEVICE_EXTENSION Extension,
|
|
IN PUNICODE_STRING DeviceName,
|
|
IN BOOLEAN IsCanonicalName,
|
|
OUT PMOUNTED_DEVICE_INFORMATION* DeviceInfo
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
This routine finds the device information for the given device.
|
|
Arguments:
|
|
Extension - Supplies the device extension.
|
|
DeviceName - Supplies the name of the device.
|
|
CanonicalizeName - Supplies whether or not the name given is canonical.
|
|
DeviceInfo - Returns the device information.
|
|
--*/
|
|
{
|
|
UNICODE_STRING targetName;
|
|
NTSTATUS status;
|
|
PLIST_ENTRY l;
|
|
PMOUNTED_DEVICE_INFORMATION deviceInfo;
|
|
|
|
if (IsCanonicalName) {
|
|
targetName = *DeviceName;
|
|
} else {
|
|
status = QueryDeviceInformation(DeviceName, &targetName, NULL, NULL, NULL);
|
|
if (!NT_SUCCESS(status)) {
|
|
return status;
|
|
}
|
|
}
|
|
|
|
for (l = Extension->MountedDeviceList.Flink; l != &Extension->MountedDeviceList; l = l->Flink) {
|
|
deviceInfo = CONTAINING_RECORD(l, MOUNTED_DEVICE_INFORMATION, ListEntry);
|
|
if (RtlEqualUnicodeString(&targetName, &deviceInfo->DeviceName, TRUE)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!IsCanonicalName) {
|
|
ExFreePool(targetName.Buffer);
|
|
}
|
|
|
|
if (l == &Extension->MountedDeviceList) {
|
|
return STATUS_OBJECT_NAME_NOT_FOUND;
|
|
}
|
|
|
|
*DeviceInfo = deviceInfo;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
QuerySuggestedLinkName(
|
|
IN PUNICODE_STRING NotificationName,
|
|
OUT PUNICODE_STRING SuggestedLinkName,
|
|
OUT PBOOLEAN UseOnlyIfThereAreNoOtherLinks
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
This routine queries the mounted device for a suggested link name.
|
|
Arguments:
|
|
NotificationName - Supplies the notification name.
|
|
SuggestedLinkName - Returns the suggested link name.
|
|
UseOnlyIfThereAreNoOtherLinks - Returns whether or not to use this name if there are other links to the device.
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
PFILE_OBJECT fileObject;
|
|
PDEVICE_OBJECT deviceObject;
|
|
ULONG outputSize;
|
|
PMOUNTDEV_SUGGESTED_LINK_NAME output;
|
|
KEVENT event;
|
|
PIRP irp;
|
|
IO_STATUS_BLOCK ioStatus;
|
|
PIO_STACK_LOCATION irpSp;
|
|
|
|
status = IoGetDeviceObjectPointer(NotificationName, FILE_READ_ATTRIBUTES, &fileObject, &deviceObject);
|
|
if (!NT_SUCCESS(status)) {
|
|
return status;
|
|
}
|
|
deviceObject = IoGetAttachedDeviceReference(fileObject->DeviceObject);
|
|
|
|
outputSize = sizeof(MOUNTDEV_SUGGESTED_LINK_NAME);
|
|
output = ExAllocatePool(PagedPool, outputSize);
|
|
if (!output) {
|
|
ObDereferenceObject(deviceObject);
|
|
ObDereferenceObject(fileObject);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
KeInitializeEvent(&event, NotificationEvent, FALSE);
|
|
irp = IoBuildDeviceIoControlRequest(IOCTL_MOUNTDEV_QUERY_SUGGESTED_LINK_NAME, deviceObject, NULL, 0, output, outputSize, FALSE, &event, &ioStatus);
|
|
if (!irp) {
|
|
ExFreePool(output);
|
|
ObDereferenceObject(deviceObject);
|
|
ObDereferenceObject(fileObject);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
irpSp = IoGetNextIrpStackLocation(irp);
|
|
irpSp->FileObject = fileObject;
|
|
|
|
status = IoCallDriver(deviceObject, irp);
|
|
if (status == STATUS_PENDING) {
|
|
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
|
|
status = ioStatus.Status;
|
|
}
|
|
|
|
if (status == STATUS_BUFFER_OVERFLOW) {
|
|
outputSize = sizeof(MOUNTDEV_SUGGESTED_LINK_NAME) + output->NameLength;
|
|
ExFreePool(output);
|
|
output = ExAllocatePool(PagedPool, outputSize);
|
|
if (!output) {
|
|
ObDereferenceObject(deviceObject);
|
|
ObDereferenceObject(fileObject);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
KeInitializeEvent(&event, NotificationEvent, FALSE);
|
|
irp = IoBuildDeviceIoControlRequest(IOCTL_MOUNTDEV_QUERY_SUGGESTED_LINK_NAME, deviceObject, NULL, 0, output, outputSize, FALSE, &event, &ioStatus);
|
|
if (!irp) {
|
|
ExFreePool(output);
|
|
ObDereferenceObject(deviceObject);
|
|
ObDereferenceObject(fileObject);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
irpSp = IoGetNextIrpStackLocation(irp);
|
|
irpSp->FileObject = fileObject;
|
|
|
|
status = IoCallDriver(deviceObject, irp);
|
|
if (status == STATUS_PENDING) {
|
|
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
|
|
status = ioStatus.Status;
|
|
}
|
|
}
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
SuggestedLinkName->Length = output->NameLength;
|
|
SuggestedLinkName->MaximumLength = output->NameLength + sizeof(WCHAR);
|
|
SuggestedLinkName->Buffer = ExAllocatePool(PagedPool, SuggestedLinkName->MaximumLength);
|
|
if (SuggestedLinkName->Buffer) {
|
|
RtlCopyMemory(SuggestedLinkName->Buffer, output->Name, output->NameLength);
|
|
SuggestedLinkName->Buffer[output->NameLength/sizeof(WCHAR)] = 0;
|
|
} else {
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
*UseOnlyIfThereAreNoOtherLinks = output->UseOnlyIfThereAreNoOtherLinks;
|
|
}
|
|
|
|
ExFreePool(output);
|
|
ObDereferenceObject(deviceObject);
|
|
ObDereferenceObject(fileObject);
|
|
return status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
SymbolicLinkNamesFromUniqueIdCount(
|
|
IN PWSTR ValueName,
|
|
IN ULONG ValueType,
|
|
IN PVOID ValueData,
|
|
IN ULONG ValueLength,
|
|
IN PVOID Context,
|
|
IN PVOID EntryContext
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
This routine counts all of the occurences of the unique id in the registry key.
|
|
Arguments:
|
|
ValueName - Supplies the name of the registry value.
|
|
ValueType - Supplies the type of the registry value.
|
|
ValueData - Supplies the data of the registry value.
|
|
ValueLength - Supplies the length of the registry value.
|
|
Context - Supplies the unique id.
|
|
EntryContext - Supplies the num names count.
|
|
--*/
|
|
{
|
|
PMOUNTDEV_UNIQUE_ID uniqueId = Context;
|
|
|
|
if (ValueName[0] == '#' || ValueType != REG_BINARY || uniqueId->UniqueIdLength != ValueLength || RtlCompareMemory(uniqueId->UniqueId, ValueData, ValueLength) != ValueLength) {
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
(*((PULONG) EntryContext))++;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
SymbolicLinkNamesFromUniqueIdQuery(
|
|
IN PWSTR ValueName,
|
|
IN ULONG ValueType,
|
|
IN PVOID ValueData,
|
|
IN ULONG ValueLength,
|
|
IN PVOID Context,
|
|
IN PVOID EntryContext
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
This routine counts all of the occurences of the unique id in the registry key.
|
|
Arguments:
|
|
ValueName - Supplies the name of the registry value.
|
|
ValueType - Supplies the type of the registry value.
|
|
ValueData - Supplies the data of the registry value.
|
|
ValueLength - Supplies the length of the registry value.
|
|
Context - Supplies the unique id.
|
|
EntryContext - Supplies the dos names array.
|
|
--*/
|
|
{
|
|
PMOUNTDEV_UNIQUE_ID uniqueId = Context;
|
|
UNICODE_STRING string;
|
|
PUNICODE_STRING p;
|
|
|
|
if (ValueName[0] == '#' || ValueType != REG_BINARY || uniqueId->UniqueIdLength != ValueLength || RtlCompareMemory(uniqueId->UniqueId, ValueData, ValueLength) != ValueLength) {
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
RtlInitUnicodeString(&string, ValueName);
|
|
string.Buffer = ExAllocatePool(PagedPool, string.MaximumLength);
|
|
if (!string.Buffer) {
|
|
return STATUS_SUCCESS;
|
|
}
|
|
RtlCopyMemory(string.Buffer, ValueName, string.Length);
|
|
string.Buffer[string.Length/sizeof(WCHAR)] = 0;
|
|
|
|
p = (PUNICODE_STRING) EntryContext;
|
|
while (p->Length != 0) {
|
|
p++;
|
|
}
|
|
|
|
*p = string;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
BOOLEAN IsDriveLetter(IN PUNICODE_STRING SymbolicLinkName)
|
|
{
|
|
UNICODE_STRING dosDevices;
|
|
|
|
if (SymbolicLinkName->Length == 28 &&
|
|
((SymbolicLinkName->Buffer[12] >= 'A' && SymbolicLinkName->Buffer[12] <= 'Z') || SymbolicLinkName->Buffer[12] == 0xFF) &&
|
|
SymbolicLinkName->Buffer[13] == ':')
|
|
{
|
|
RtlInitUnicodeString (&dosDevices, L"\\DosDevices\\");
|
|
|
|
SymbolicLinkName->Length = 24;
|
|
if (RtlEqualUnicodeString (SymbolicLinkName, &dosDevices, TRUE))
|
|
{
|
|
SymbolicLinkName->Length = 28;
|
|
return TRUE;
|
|
}
|
|
SymbolicLinkName->Length = 28;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
QuerySymbolicLinkNamesFromStorage(
|
|
IN PDEVICE_EXTENSION Extension,
|
|
IN PMOUNTED_DEVICE_INFORMATION DeviceInfo,
|
|
IN PUNICODE_STRING SuggestedName,
|
|
IN BOOLEAN UseOnlyIfThereAreNoOtherLinks,
|
|
OUT PUNICODE_STRING* SymbolicLinkNames,
|
|
OUT PULONG NumNames
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
This routine queries the symbolic link names from storage for the given notification name.
|
|
Arguments:
|
|
Extension - Supplies the device extension.
|
|
DeviceInfo - Supplies the device information.
|
|
SymbolicLinkNames - Returns the symbolic link names.
|
|
NumNames - Returns the number of symbolic link names.
|
|
--*/
|
|
{
|
|
RTL_QUERY_REGISTRY_TABLE queryTable[2];
|
|
BOOLEAN extraLink;
|
|
NTSTATUS status;
|
|
|
|
RtlZeroMemory(queryTable, 2*sizeof(RTL_QUERY_REGISTRY_TABLE));
|
|
queryTable[0].QueryRoutine = SymbolicLinkNamesFromUniqueIdCount;
|
|
queryTable[0].Flags = RTL_QUERY_REGISTRY_REQUIRED;
|
|
queryTable[0].EntryContext = NumNames;
|
|
|
|
*NumNames = 0;
|
|
status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE, MOUNTED_DEVICES_KEY, queryTable, DeviceInfo->UniqueId, NULL);
|
|
if (!NT_SUCCESS(status)) {
|
|
*NumNames = 0;
|
|
}
|
|
|
|
if (SuggestedName && !IsDriveLetter(SuggestedName)) {
|
|
if (UseOnlyIfThereAreNoOtherLinks) {
|
|
if (*NumNames == 0) {
|
|
extraLink = TRUE;
|
|
} else {
|
|
extraLink = FALSE;
|
|
}
|
|
} else {
|
|
extraLink = TRUE;
|
|
}
|
|
} else {
|
|
extraLink = FALSE;
|
|
}
|
|
|
|
if (extraLink) {
|
|
RtlWriteRegistryValue(RTL_REGISTRY_ABSOLUTE, MOUNTED_DEVICES_KEY, SuggestedName->Buffer, REG_BINARY, DeviceInfo->UniqueId->UniqueId, DeviceInfo->UniqueId->UniqueIdLength);
|
|
|
|
RtlZeroMemory(queryTable, 2*sizeof(RTL_QUERY_REGISTRY_TABLE));
|
|
queryTable[0].QueryRoutine = SymbolicLinkNamesFromUniqueIdCount;
|
|
queryTable[0].Flags = RTL_QUERY_REGISTRY_REQUIRED;
|
|
queryTable[0].EntryContext = NumNames;
|
|
|
|
*NumNames = 0;
|
|
status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE, MOUNTED_DEVICES_KEY, queryTable, DeviceInfo->UniqueId, NULL);
|
|
|
|
if (!NT_SUCCESS(status) || *NumNames == 0) {
|
|
return STATUS_NOT_FOUND;
|
|
}
|
|
} else if (!*NumNames) {
|
|
return STATUS_NOT_FOUND;
|
|
}
|
|
|
|
*SymbolicLinkNames = ExAllocatePool(PagedPool, *NumNames*sizeof(UNICODE_STRING));
|
|
if (!*SymbolicLinkNames) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
RtlZeroMemory(*SymbolicLinkNames, *NumNames*sizeof(UNICODE_STRING));
|
|
|
|
RtlZeroMemory(queryTable, 2*sizeof(RTL_QUERY_REGISTRY_TABLE));
|
|
queryTable[0].QueryRoutine = SymbolicLinkNamesFromUniqueIdQuery;
|
|
queryTable[0].Flags = RTL_QUERY_REGISTRY_REQUIRED;
|
|
queryTable[0].EntryContext = *SymbolicLinkNames;
|
|
|
|
status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE, MOUNTED_DEVICES_KEY, queryTable, DeviceInfo->UniqueId, NULL);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
ChangeUniqueIdRoutine(
|
|
IN PWSTR ValueName,
|
|
IN ULONG ValueType,
|
|
IN PVOID ValueData,
|
|
IN ULONG ValueLength,
|
|
IN PVOID Context,
|
|
IN PVOID EntryContext
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
This routine replaces all old unique ids with new unique ids.
|
|
Arguments:
|
|
ValueName - Supplies the name of the registry value.
|
|
ValueType - Supplies the type of the registry value.
|
|
ValueData - Supplies the data of the registry value.
|
|
ValueLength - Supplies the length of the registry value.
|
|
Context - Supplies the old unique id.
|
|
EntryContext - Supplies the new unique id.
|
|
--*/
|
|
{
|
|
PMOUNTDEV_UNIQUE_ID oldId = Context;
|
|
PMOUNTDEV_UNIQUE_ID newId = EntryContext;
|
|
|
|
if (ValueType != REG_BINARY || oldId->UniqueIdLength != ValueLength || RtlCompareMemory(oldId->UniqueId, ValueData, ValueLength) != ValueLength) {
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
RtlWriteRegistryValue(RTL_REGISTRY_ABSOLUTE, MOUNTED_DEVICES_KEY, ValueName, ValueType, newId->UniqueId, newId->UniqueIdLength);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
HANDLE
|
|
OpenRemoteDatabase(
|
|
IN PUNICODE_STRING RemoteDatabaseVolumeName,
|
|
IN BOOLEAN Create
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
This routine opens the remote database on the given volume.
|
|
Arguments:
|
|
RemoteDatabaseVolumeName - Supplies the remote database volume name.
|
|
Create - Supplies whether or not to create.
|
|
Return Value:
|
|
A handle to the remote database or NULL.
|
|
--*/
|
|
{
|
|
UNICODE_STRING suffix;
|
|
UNICODE_STRING fileName;
|
|
OBJECT_ATTRIBUTES oa;
|
|
NTSTATUS status;
|
|
HANDLE h;
|
|
IO_STATUS_BLOCK ioStatus;
|
|
|
|
RtlInitUnicodeString(&suffix, L"\\:$MountMgrRemoteDatabase");
|
|
|
|
fileName.Length = RemoteDatabaseVolumeName->Length + suffix.Length;
|
|
fileName.MaximumLength = fileName.Length + sizeof(WCHAR);
|
|
fileName.Buffer = ExAllocatePool(PagedPool, fileName.MaximumLength);
|
|
if (!fileName.Buffer) {
|
|
return NULL;
|
|
}
|
|
|
|
RtlCopyMemory(fileName.Buffer, RemoteDatabaseVolumeName->Buffer, RemoteDatabaseVolumeName->Length);
|
|
RtlCopyMemory((PCHAR) fileName.Buffer + RemoteDatabaseVolumeName->Length, suffix.Buffer, suffix.Length);
|
|
fileName.Buffer[fileName.Length/sizeof(WCHAR)] = 0;
|
|
|
|
InitializeObjectAttributes(&oa, &fileName, OBJ_CASE_INSENSITIVE, 0, 0);
|
|
|
|
status = ZwCreateFile(&h,
|
|
FILE_GENERIC_READ | FILE_GENERIC_WRITE,
|
|
&oa,
|
|
&ioStatus,
|
|
NULL,
|
|
FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM,
|
|
0,
|
|
Create ? FILE_OPEN_IF : FILE_OPEN,
|
|
FILE_SYNCHRONOUS_IO_ALERT | FILE_NON_DIRECTORY_FILE,
|
|
NULL,
|
|
0);
|
|
ExFreePool(fileName.Buffer);
|
|
if (!NT_SUCCESS(status)) {
|
|
return NULL;
|
|
}
|
|
|
|
return h;
|
|
}
|
|
|
|
|
|
ULONG GetRemoteDatabaseSize(IN HANDLE RemoteDatabaseHandle)
|
|
/*++
|
|
Routine Description:
|
|
This routine returns the length of the remote database.
|
|
Arguments:
|
|
RemoteDatabaseHandle - Supplies a handle to the remote database.
|
|
Return Value:
|
|
The length of the remote database or 0.
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
IO_STATUS_BLOCK ioStatus;
|
|
FILE_STANDARD_INFORMATION info;
|
|
|
|
status = ZwQueryInformationFile(RemoteDatabaseHandle, &ioStatus, &info, sizeof(info), FileStandardInformation);
|
|
if (!NT_SUCCESS(status)) {
|
|
return 0;
|
|
}
|
|
|
|
return info.EndOfFile.LowPart;
|
|
}
|
|
|
|
|
|
VOID CloseRemoteDatabase(IN HANDLE RemoteDatabaseHandle)
|
|
/*++
|
|
Routine Description:
|
|
This routine closes the given remote database.
|
|
Arguments:
|
|
RemoteDatabaseHandle - Supplies a handle to the remote database.
|
|
--*/
|
|
{
|
|
ULONG fileLength;
|
|
FILE_DISPOSITION_INFORMATION disp;
|
|
IO_STATUS_BLOCK ioStatus;
|
|
|
|
fileLength = GetRemoteDatabaseSize(RemoteDatabaseHandle);
|
|
if (!fileLength) {
|
|
disp.DeleteFile = TRUE;
|
|
ZwSetInformationFile(RemoteDatabaseHandle, &ioStatus, &disp, sizeof(disp), FileDispositionInformation);
|
|
}
|
|
|
|
ZwClose(RemoteDatabaseHandle);
|
|
}
|
|
|
|
|
|
NTSTATUS TruncateRemoteDatabase(IN HANDLE RemoteDatabaseHandle, IN ULONG FileOffset)
|
|
/*++
|
|
Routine Description:
|
|
This routine truncates the remote database at the given file offset.
|
|
Arguments:
|
|
RemoteDatabaseHandle - Supplies a handle to the remote database.
|
|
FileOffset - Supplies the file offset.
|
|
--*/
|
|
{
|
|
FILE_END_OF_FILE_INFORMATION endOfFileInfo;
|
|
FILE_ALLOCATION_INFORMATION allocationInfo;
|
|
NTSTATUS status;
|
|
IO_STATUS_BLOCK ioStatus;
|
|
|
|
endOfFileInfo.EndOfFile.QuadPart = FileOffset;
|
|
allocationInfo.AllocationSize.QuadPart = FileOffset;
|
|
|
|
status = ZwSetInformationFile(RemoteDatabaseHandle, &ioStatus, &endOfFileInfo, sizeof(endOfFileInfo), FileEndOfFileInformation);
|
|
if (!NT_SUCCESS(status)) {
|
|
return status;
|
|
}
|
|
|
|
status = ZwSetInformationFile(RemoteDatabaseHandle, &ioStatus, &allocationInfo, sizeof(allocationInfo), FileAllocationInformation);
|
|
return status;
|
|
}
|
|
|
|
|
|
PMOUNTMGR_FILE_ENTRY GetRemoteDatabaseEntry(IN HANDLE RemoteDatabaseHandle, IN ULONG FileOffset)
|
|
/*++
|
|
Routine Description:
|
|
This routine gets the next database entry.
|
|
This routine fixes corruption as it finds it.
|
|
The memory returned from this routine must be freed with ExFreePool.
|
|
Arguments:
|
|
RemoteDatabaseHandle - Supplies a handle to the remote database.
|
|
FileOffset - Supplies the file offset.
|
|
Return Value:
|
|
A pointer to the next remote database entry.
|
|
--*/
|
|
{
|
|
LARGE_INTEGER offset;
|
|
NTSTATUS status;
|
|
IO_STATUS_BLOCK ioStatus;
|
|
ULONG size;
|
|
PMOUNTMGR_FILE_ENTRY entry;
|
|
ULONG len1, len2, len;
|
|
|
|
offset.QuadPart = FileOffset;
|
|
status = ZwReadFile(RemoteDatabaseHandle, NULL, NULL, NULL, &ioStatus,
|
|
&size, sizeof(size), &offset, NULL);
|
|
if (!NT_SUCCESS(status)) {
|
|
return NULL;
|
|
}
|
|
if (!size) {
|
|
TruncateRemoteDatabase(RemoteDatabaseHandle, FileOffset);
|
|
return NULL;
|
|
}
|
|
|
|
entry = ExAllocatePool(PagedPool, size);
|
|
if (!entry) {
|
|
return NULL;
|
|
}
|
|
|
|
status = ZwReadFile(RemoteDatabaseHandle, NULL, NULL, NULL, &ioStatus, entry, size, &offset, NULL);
|
|
if (!NT_SUCCESS(status)) {
|
|
TruncateRemoteDatabase(RemoteDatabaseHandle, FileOffset);
|
|
ExFreePool(entry);
|
|
return NULL;
|
|
}
|
|
|
|
if (ioStatus.Information < size) {
|
|
TruncateRemoteDatabase(RemoteDatabaseHandle, FileOffset);
|
|
ExFreePool(entry);
|
|
return NULL;
|
|
}
|
|
|
|
if (size < sizeof(MOUNTMGR_FILE_ENTRY)) {
|
|
TruncateRemoteDatabase(RemoteDatabaseHandle, FileOffset);
|
|
ExFreePool(entry);
|
|
return NULL;
|
|
}
|
|
|
|
len1 = entry->VolumeNameOffset + entry->VolumeNameLength;
|
|
len2 = entry->UniqueIdOffset + entry->UniqueIdLength;
|
|
len = len1 > len2 ? len1 : len2;
|
|
|
|
if (len > size) {
|
|
TruncateRemoteDatabase(RemoteDatabaseHandle, FileOffset);
|
|
ExFreePool(entry);
|
|
return NULL;
|
|
}
|
|
|
|
return entry;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
WriteRemoteDatabaseEntry(
|
|
IN HANDLE RemoteDatabaseHandle,
|
|
IN ULONG FileOffset,
|
|
IN PMOUNTMGR_FILE_ENTRY DatabaseEntry
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
This routine write the given database entry at the given file offset to the remote database.
|
|
Arguments:
|
|
RemoteDatabaseHandle - Supplies a handle to the remote database.
|
|
FileOffset - Supplies the file offset.
|
|
DatabaseEntry - Supplies the database entry.
|
|
Return Value:
|
|
NTSTATUS
|
|
--*/
|
|
{
|
|
LARGE_INTEGER offset;
|
|
NTSTATUS status;
|
|
IO_STATUS_BLOCK ioStatus;
|
|
|
|
offset.QuadPart = FileOffset;
|
|
status = ZwWriteFile(RemoteDatabaseHandle, NULL, NULL, NULL, &ioStatus, DatabaseEntry, DatabaseEntry->EntryLength, &offset, NULL);
|
|
if (!NT_SUCCESS(status)) {
|
|
return status;
|
|
}
|
|
|
|
if (ioStatus.Information < DatabaseEntry->EntryLength) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
DeleteRemoteDatabaseEntry(
|
|
IN HANDLE RemoteDatabaseHandle,
|
|
IN ULONG FileOffset
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
This routine deletes the database entry at the given file offset in the remote database.
|
|
Arguments:
|
|
RemoteDatabaseHandle - Supplies a handle to the remote database.
|
|
FileOffset - Supplies the file offset.
|
|
Return Value:
|
|
NTSTATUS
|
|
--*/
|
|
{
|
|
ULONG fileSize;
|
|
PMOUNTMGR_FILE_ENTRY entry;
|
|
LARGE_INTEGER offset;
|
|
ULONG size;
|
|
PVOID buffer;
|
|
NTSTATUS status;
|
|
IO_STATUS_BLOCK ioStatus;
|
|
|
|
fileSize = GetRemoteDatabaseSize(RemoteDatabaseHandle);
|
|
if (!fileSize) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
entry = GetRemoteDatabaseEntry(RemoteDatabaseHandle, FileOffset);
|
|
if (!entry) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (FileOffset + entry->EntryLength >= fileSize) {
|
|
ExFreePool(entry);
|
|
return TruncateRemoteDatabase(RemoteDatabaseHandle, FileOffset);
|
|
}
|
|
|
|
size = fileSize - FileOffset - entry->EntryLength;
|
|
buffer = ExAllocatePool(PagedPool, size);
|
|
if (!buffer) {
|
|
ExFreePool(entry);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
offset.QuadPart = FileOffset + entry->EntryLength;
|
|
ExFreePool(entry);
|
|
|
|
status = ZwReadFile(RemoteDatabaseHandle, NULL, NULL, NULL, &ioStatus, buffer, size, &offset, NULL);
|
|
if (!NT_SUCCESS(status)) {
|
|
ExFreePool(buffer);
|
|
return status;
|
|
}
|
|
|
|
if (ioStatus.Information < size) {
|
|
ExFreePool(buffer);
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
status = TruncateRemoteDatabase(RemoteDatabaseHandle, FileOffset);
|
|
if (!NT_SUCCESS(status)) {
|
|
ExFreePool(buffer);
|
|
return status;
|
|
}
|
|
|
|
offset.QuadPart = FileOffset;
|
|
status = ZwWriteFile(RemoteDatabaseHandle, NULL, NULL, NULL, &ioStatus, buffer, size, &offset, NULL);
|
|
ExFreePool(buffer);
|
|
return status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
AddRemoteDatabaseEntry(
|
|
IN HANDLE RemoteDatabaseHandle,
|
|
IN PMOUNTMGR_FILE_ENTRY DatabaseEntry
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
This routine adds a new database entry to the remote database.
|
|
Arguments:
|
|
RemoteDatabaseHandle - Supplies a handle to the remote database.
|
|
DatabaseEntry - Supplies the database entry.
|
|
Return Value:
|
|
NTSTATUS
|
|
--*/
|
|
{
|
|
ULONG fileSize;
|
|
LARGE_INTEGER offset;
|
|
NTSTATUS status;
|
|
IO_STATUS_BLOCK ioStatus;
|
|
|
|
fileSize = GetRemoteDatabaseSize(RemoteDatabaseHandle);
|
|
offset.QuadPart = fileSize;
|
|
status = ZwWriteFile(RemoteDatabaseHandle, NULL, NULL, NULL, &ioStatus, DatabaseEntry, DatabaseEntry->EntryLength, &offset, NULL);
|
|
return status;
|
|
}
|
|
|
|
|
|
VOID
|
|
ChangeRemoteDatabaseUniqueId(
|
|
IN PUNICODE_STRING RemoteDatabaseVolumeName,
|
|
IN PMOUNTDEV_UNIQUE_ID OldUniqueId,
|
|
IN PMOUNTDEV_UNIQUE_ID NewUniqueId
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
This routine changes the unique id in the remote database.
|
|
Arguments:
|
|
RemoteDatabaseVolumeName - Supplies the remote database volume name.
|
|
OldUniqueId - Supplies the old unique id.
|
|
NewUniqueId - Supplies the new unique id.
|
|
--*/
|
|
{
|
|
HANDLE h;
|
|
ULONG offset, newSize;
|
|
PMOUNTMGR_FILE_ENTRY databaseEntry, newDatabaseEntry;
|
|
NTSTATUS status;
|
|
|
|
h = OpenRemoteDatabase(RemoteDatabaseVolumeName, FALSE);
|
|
if (!h) {
|
|
return;
|
|
}
|
|
|
|
offset = 0;
|
|
for (;;) {
|
|
databaseEntry = GetRemoteDatabaseEntry(h, offset);
|
|
if (!databaseEntry) {
|
|
break;
|
|
}
|
|
|
|
if (databaseEntry->UniqueIdLength != OldUniqueId->UniqueIdLength ||
|
|
RtlCompareMemory(OldUniqueId->UniqueId, (PCHAR) databaseEntry + databaseEntry->UniqueIdOffset, databaseEntry->UniqueIdLength) != databaseEntry->UniqueIdLength) {
|
|
offset += databaseEntry->EntryLength;
|
|
ExFreePool(databaseEntry);
|
|
continue;
|
|
}
|
|
|
|
newSize = databaseEntry->EntryLength + NewUniqueId->UniqueIdLength - OldUniqueId->UniqueIdLength;
|
|
newDatabaseEntry = ExAllocatePool(PagedPool, newSize);
|
|
if (!newDatabaseEntry) {
|
|
offset += databaseEntry->EntryLength;
|
|
ExFreePool(databaseEntry);
|
|
continue;
|
|
}
|
|
|
|
newDatabaseEntry->EntryLength = newSize;
|
|
newDatabaseEntry->RefCount = databaseEntry->RefCount;
|
|
newDatabaseEntry->VolumeNameOffset = sizeof(MOUNTMGR_FILE_ENTRY);
|
|
newDatabaseEntry->VolumeNameLength = databaseEntry->VolumeNameLength;
|
|
newDatabaseEntry->UniqueIdOffset = newDatabaseEntry->VolumeNameOffset + newDatabaseEntry->VolumeNameLength;
|
|
newDatabaseEntry->UniqueIdLength = NewUniqueId->UniqueIdLength;
|
|
|
|
RtlCopyMemory((PCHAR) newDatabaseEntry + newDatabaseEntry->VolumeNameOffset, (PCHAR) databaseEntry + databaseEntry->VolumeNameOffset, newDatabaseEntry->VolumeNameLength);
|
|
RtlCopyMemory((PCHAR) newDatabaseEntry + newDatabaseEntry->UniqueIdOffset, NewUniqueId->UniqueId, newDatabaseEntry->UniqueIdLength);
|
|
|
|
status = DeleteRemoteDatabaseEntry(h, offset);
|
|
if (!NT_SUCCESS(status)) {
|
|
ExFreePool(databaseEntry);
|
|
ExFreePool(newDatabaseEntry);
|
|
break;
|
|
}
|
|
|
|
status = AddRemoteDatabaseEntry(h, newDatabaseEntry);
|
|
if (!NT_SUCCESS(status)) {
|
|
ExFreePool(databaseEntry);
|
|
ExFreePool(newDatabaseEntry);
|
|
break;
|
|
}
|
|
|
|
ExFreePool(newDatabaseEntry);
|
|
ExFreePool(databaseEntry);
|
|
}
|
|
|
|
CloseRemoteDatabase(h);
|
|
}
|
|
|
|
|
|
NTSTATUS WaitForRemoteDatabaseSemaphore(IN PDEVICE_EXTENSION Extension)
|
|
{
|
|
LARGE_INTEGER timeout;
|
|
NTSTATUS status;
|
|
|
|
timeout.QuadPart = -10*1000*1000*10;
|
|
status = KeWaitForSingleObject(&Extension->RemoteDatabaseSemaphore, Executive, KernelMode, FALSE, &timeout);
|
|
if (status == STATUS_TIMEOUT) {
|
|
status = STATUS_IO_TIMEOUT;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
VOID ReleaseRemoteDatabaseSemaphore(IN PDEVICE_EXTENSION Extension)
|
|
{
|
|
KeReleaseSemaphore(&Extension->RemoteDatabaseSemaphore, IO_NO_INCREMENT, 1, FALSE);
|
|
}
|
|
|
|
|
|
VOID
|
|
MountMgrUniqueIdChangeRoutine(
|
|
IN PVOID Context,
|
|
IN PMOUNTDEV_UNIQUE_ID OldUniqueId,
|
|
IN PMOUNTDEV_UNIQUE_ID NewUniqueId
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
This routine is called from a mounted device to notify of a unique id change.
|
|
Arguments:
|
|
MountedDevice - Supplies the mounted device.
|
|
MountMgrUniqueIdChangeRoutine - Supplies the id change routine.
|
|
Context - Supplies the context for this routine.
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
PDEVICE_EXTENSION extension = Context;
|
|
RTL_QUERY_REGISTRY_TABLE queryTable[2];
|
|
PLIST_ENTRY l, ll;
|
|
PMOUNTED_DEVICE_INFORMATION deviceInfo;
|
|
PREPLICATED_UNIQUE_ID replUniqueId;
|
|
PVOID p;
|
|
BOOLEAN changedIds;
|
|
|
|
status = WaitForRemoteDatabaseSemaphore(extension);
|
|
|
|
KeWaitForSingleObject(&extension->Mutex, Executive, KernelMode, FALSE, NULL);
|
|
|
|
RtlZeroMemory(queryTable, 2*sizeof(RTL_QUERY_REGISTRY_TABLE));
|
|
queryTable[0].QueryRoutine = ChangeUniqueIdRoutine;
|
|
queryTable[0].Flags = RTL_QUERY_REGISTRY_REQUIRED;
|
|
queryTable[0].EntryContext = NewUniqueId;
|
|
|
|
RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE, MOUNTED_DEVICES_KEY, queryTable, OldUniqueId, NULL);
|
|
|
|
for (l = extension->MountedDeviceList.Flink; l != &extension->MountedDeviceList; l = l->Flink) {
|
|
deviceInfo = CONTAINING_RECORD(l, MOUNTED_DEVICE_INFORMATION, ListEntry);
|
|
if (OldUniqueId->UniqueIdLength != deviceInfo->UniqueId->UniqueIdLength) {
|
|
continue;
|
|
}
|
|
|
|
if (RtlCompareMemory(OldUniqueId->UniqueId, deviceInfo->UniqueId->UniqueId, OldUniqueId->UniqueIdLength) != OldUniqueId->UniqueIdLength) {
|
|
continue;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
if (l == &extension->MountedDeviceList) {
|
|
KeReleaseSemaphore(&extension->Mutex, IO_NO_INCREMENT, 1, FALSE);
|
|
if (NT_SUCCESS(status)) {
|
|
ReleaseRemoteDatabaseSemaphore(extension);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
ReconcileThisDatabaseWithMaster(extension, deviceInfo);
|
|
KeReleaseSemaphore(&extension->Mutex, IO_NO_INCREMENT, 1, FALSE);
|
|
return;
|
|
}
|
|
|
|
p = ExAllocatePool(PagedPool, NewUniqueId->UniqueIdLength + sizeof(MOUNTDEV_UNIQUE_ID));
|
|
if (!p) {
|
|
KeReleaseSemaphore(&extension->Mutex, IO_NO_INCREMENT, 1, FALSE);
|
|
ReleaseRemoteDatabaseSemaphore(extension);
|
|
return;
|
|
}
|
|
ExFreePool(deviceInfo->UniqueId);
|
|
deviceInfo->UniqueId = p;
|
|
|
|
deviceInfo->UniqueId->UniqueIdLength = NewUniqueId->UniqueIdLength;
|
|
RtlCopyMemory(deviceInfo->UniqueId->UniqueId, NewUniqueId->UniqueId, NewUniqueId->UniqueIdLength);
|
|
|
|
for (l = extension->MountedDeviceList.Flink; l != &extension->MountedDeviceList; l = l->Flink) {
|
|
deviceInfo = CONTAINING_RECORD(l, MOUNTED_DEVICE_INFORMATION, ListEntry);
|
|
changedIds = FALSE;
|
|
for (ll = deviceInfo->ReplicatedUniqueIds.Flink; ll != &deviceInfo->ReplicatedUniqueIds; ll = ll->Flink) {
|
|
replUniqueId = CONTAINING_RECORD(ll, REPLICATED_UNIQUE_ID, ListEntry);
|
|
if (replUniqueId->UniqueId->UniqueIdLength != OldUniqueId->UniqueIdLength) {
|
|
continue;
|
|
}
|
|
|
|
if (RtlCompareMemory(replUniqueId->UniqueId->UniqueId, OldUniqueId->UniqueId, OldUniqueId->UniqueIdLength) != OldUniqueId->UniqueIdLength) {
|
|
continue;
|
|
}
|
|
|
|
p = ExAllocatePool(PagedPool, NewUniqueId->UniqueIdLength + sizeof(MOUNTDEV_UNIQUE_ID));
|
|
if (!p) {
|
|
continue;
|
|
}
|
|
|
|
changedIds = TRUE;
|
|
ExFreePool(replUniqueId->UniqueId);
|
|
replUniqueId->UniqueId = p;
|
|
replUniqueId->UniqueId->UniqueIdLength = NewUniqueId->UniqueIdLength;
|
|
RtlCopyMemory(replUniqueId->UniqueId->UniqueId, NewUniqueId->UniqueId, NewUniqueId->UniqueIdLength);
|
|
}
|
|
|
|
if (changedIds) {
|
|
ChangeRemoteDatabaseUniqueId(&deviceInfo->DeviceName, OldUniqueId, NewUniqueId);
|
|
}
|
|
}
|
|
|
|
KeReleaseSemaphore(&extension->Mutex, IO_NO_INCREMENT, 1, FALSE);
|
|
ReleaseRemoteDatabaseSemaphore(extension);
|
|
}
|
|
|
|
|
|
VOID SendLinkCreated(IN PUNICODE_STRING SymbolicLinkName)
|
|
/*++
|
|
Routine Description:
|
|
This routine alerts the mounted device that one of its links has been created
|
|
Arguments:
|
|
SymbolicLinkName - Supplies the symbolic link name being deleted.
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
PFILE_OBJECT fileObject;
|
|
PDEVICE_OBJECT deviceObject;
|
|
ULONG inputSize;
|
|
PMOUNTDEV_NAME input;
|
|
KEVENT event;
|
|
PIRP irp;
|
|
IO_STATUS_BLOCK ioStatus;
|
|
PIO_STACK_LOCATION irpSp;
|
|
|
|
status = IoGetDeviceObjectPointer(SymbolicLinkName, FILE_READ_ATTRIBUTES, &fileObject, &deviceObject);
|
|
if (!NT_SUCCESS(status)) {
|
|
return;
|
|
}
|
|
deviceObject = IoGetAttachedDeviceReference(fileObject->DeviceObject);
|
|
|
|
inputSize = sizeof(USHORT) + SymbolicLinkName->Length;
|
|
input = ExAllocatePool(PagedPool, inputSize);
|
|
if (!input) {
|
|
ObDereferenceObject(deviceObject);
|
|
ObDereferenceObject(fileObject);
|
|
return;
|
|
}
|
|
|
|
input->NameLength = SymbolicLinkName->Length;
|
|
RtlCopyMemory(input->Name, SymbolicLinkName->Buffer, SymbolicLinkName->Length);
|
|
|
|
KeInitializeEvent(&event, NotificationEvent, FALSE);
|
|
irp = IoBuildDeviceIoControlRequest(IOCTL_MOUNTDEV_LINK_CREATED, deviceObject, input, inputSize, NULL, 0, FALSE, &event, &ioStatus);
|
|
if (!irp) {
|
|
ObDereferenceObject(deviceObject);
|
|
ObDereferenceObject(fileObject);
|
|
return;
|
|
}
|
|
irpSp = IoGetNextIrpStackLocation(irp);
|
|
irpSp->FileObject = fileObject;
|
|
|
|
status = IoCallDriver(deviceObject, irp);
|
|
if (status == STATUS_PENDING) {
|
|
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
|
|
status = ioStatus.Status;
|
|
}
|
|
|
|
ObDereferenceObject(deviceObject);
|
|
ObDereferenceObject(fileObject);
|
|
}
|
|
|
|
|
|
NTSTATUS CreateNewVolumeName(OUT PUNICODE_STRING VolumeName)
|
|
/*++
|
|
Routine Description:
|
|
This routine creates a new name of the form \??\Volume{GUID}.
|
|
Arguments:
|
|
VolumeName - Returns the volume name.
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
UUID uuid;
|
|
UNICODE_STRING guidString, prefix;
|
|
|
|
status = ExUuidCreate(&uuid);
|
|
if (!NT_SUCCESS(status)) {
|
|
return status;
|
|
}
|
|
|
|
status = RtlStringFromGUID(&uuid, &guidString);
|
|
if (!NT_SUCCESS(status)) {
|
|
return status;
|
|
}
|
|
|
|
VolumeName->MaximumLength = 98;
|
|
VolumeName->Buffer = ExAllocatePool(PagedPool, VolumeName->MaximumLength);
|
|
if (!VolumeName->Buffer) {
|
|
ExFreePool(guidString.Buffer);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
RtlInitUnicodeString(&prefix, L"\\??\\Volume");
|
|
RtlCopyUnicodeString(VolumeName, &prefix);
|
|
RtlAppendUnicodeStringToString(VolumeName, &guidString);
|
|
VolumeName->Buffer[VolumeName->Length/sizeof(WCHAR)] = 0;
|
|
|
|
ExFreePool(guidString.Buffer);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
VOID CreateNoDriveLetterEntry(IN PMOUNTDEV_UNIQUE_ID UniqueId)
|
|
/*++
|
|
Routine Description:
|
|
This routine creates a "no drive letter" entry for the given device.
|
|
Arguments:
|
|
UniqueId - Supplies the unique id.
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
UUID uuid;
|
|
UNICODE_STRING guidString;
|
|
PWSTR valueName;
|
|
|
|
status = ExUuidCreate(&uuid);
|
|
if (!NT_SUCCESS(status)) {
|
|
return;
|
|
}
|
|
|
|
status = RtlStringFromGUID(&uuid, &guidString);
|
|
if (!NT_SUCCESS(status)) {
|
|
return;
|
|
}
|
|
|
|
valueName = ExAllocatePool(PagedPool, guidString.Length + 2*sizeof(WCHAR));
|
|
if (!valueName) {
|
|
ExFreePool(guidString.Buffer);
|
|
return;
|
|
}
|
|
|
|
valueName[0] = '#';
|
|
RtlCopyMemory(&valueName[1], guidString.Buffer, guidString.Length);
|
|
valueName[1 + guidString.Length/sizeof(WCHAR)] = 0;
|
|
ExFreePool(guidString.Buffer);
|
|
|
|
RtlWriteRegistryValue(RTL_REGISTRY_ABSOLUTE, MOUNTED_DEVICES_KEY, valueName, REG_BINARY, UniqueId->UniqueId, UniqueId->UniqueIdLength);
|
|
ExFreePool(valueName);
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
CreateNewDriveLetterName(
|
|
OUT PUNICODE_STRING DriveLetterName,
|
|
IN PUNICODE_STRING TargetName,
|
|
IN UCHAR SuggestedDriveLetter,
|
|
IN PMOUNTDEV_UNIQUE_ID UniqueId
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
This routine creates a new name of the form \DosDevices\D:.
|
|
Arguments:
|
|
DriveLetterName - Returns the drive letter name.
|
|
TargetName - Supplies the target object.
|
|
SuggestedDriveLetter - Supplies the suggested drive letter.
|
|
Return Value:
|
|
NTSTATUS
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
UNICODE_STRING prefix, floppyPrefix, cdromPrefix;
|
|
UCHAR driveLetter;
|
|
|
|
DriveLetterName->MaximumLength = 30;
|
|
DriveLetterName->Buffer = ExAllocatePool(PagedPool, DriveLetterName->MaximumLength);
|
|
if (!DriveLetterName->Buffer) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
RtlInitUnicodeString(&prefix, L"\\DosDevices\\");
|
|
RtlCopyUnicodeString(DriveLetterName, &prefix);
|
|
|
|
DriveLetterName->Length = 28;
|
|
DriveLetterName->Buffer[14] = 0;
|
|
DriveLetterName->Buffer[13] = ':';
|
|
|
|
if (SuggestedDriveLetter == 0xFF) {
|
|
CreateNoDriveLetterEntry(UniqueId);
|
|
ExFreePool(DriveLetterName->Buffer);
|
|
return STATUS_UNSUCCESSFUL;
|
|
} else if (SuggestedDriveLetter) {
|
|
DriveLetterName->Buffer[12] = SuggestedDriveLetter;
|
|
status = IoCreateSymbolicLink(DriveLetterName, TargetName);
|
|
if (NT_SUCCESS(status)) {
|
|
return status;
|
|
}
|
|
}
|
|
|
|
RtlInitUnicodeString(&floppyPrefix, L"\\Device\\Floppy");
|
|
RtlInitUnicodeString(&cdromPrefix, L"\\Device\\CdRom");
|
|
if (RtlPrefixUnicodeString(&floppyPrefix, TargetName, TRUE)) {
|
|
driveLetter = 'A';
|
|
} else if (RtlPrefixUnicodeString(&cdromPrefix, TargetName, TRUE)) {
|
|
driveLetter = 'D';
|
|
} else {
|
|
driveLetter = 'C';
|
|
}
|
|
|
|
for (; driveLetter <= 'Z'; driveLetter++) {
|
|
DriveLetterName->Buffer[12] = driveLetter;
|
|
|
|
status = IoCreateSymbolicLink(DriveLetterName, TargetName);
|
|
if (NT_SUCCESS(status)) {
|
|
return status;
|
|
}
|
|
}
|
|
|
|
ExFreePool(DriveLetterName->Buffer);
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
CheckForNoDriveLetterEntry(
|
|
IN PWSTR ValueName,
|
|
IN ULONG ValueType,
|
|
IN PVOID ValueData,
|
|
IN ULONG ValueLength,
|
|
IN PVOID Context,
|
|
IN PVOID EntryContext
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
This routine checks for the presence of the "no drive letter" entry.
|
|
Arguments:
|
|
ValueName - Supplies the name of the registry value.
|
|
ValueType - Supplies the type of the registry value.
|
|
ValueData - Supplies the data of the registry value.
|
|
ValueLength - Supplies the length of the registry value.
|
|
Context - Supplies the unique id.
|
|
EntryContext - Returns whether or not there is a "no drive letter" entry.
|
|
Return Value:
|
|
NTSTATUS
|
|
--*/
|
|
{
|
|
PMOUNTDEV_UNIQUE_ID uniqueId = Context;
|
|
|
|
if (ValueName[0] != '#' || ValueType != REG_BINARY || ValueLength != uniqueId->UniqueIdLength || RtlCompareMemory(uniqueId->UniqueId, ValueData, ValueLength) != ValueLength) {
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
*((PBOOLEAN) EntryContext) = TRUE;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
BOOLEAN HasNoDriveLetterEntry(IN PMOUNTDEV_UNIQUE_ID UniqueId)
|
|
/*++
|
|
Routine Description:
|
|
This routine determines whether or not the given device has an entry indicating that it should not receice a drive letter.
|
|
Arguments:
|
|
UniqueId - Supplies the unique id.
|
|
Return Value:
|
|
FALSE - The device does not have a "no drive letter" entry.
|
|
TRUE - The device has a "no drive letter" entry.
|
|
--*/
|
|
{
|
|
RTL_QUERY_REGISTRY_TABLE queryTable[2];
|
|
BOOLEAN hasNoDriveLetterEntry;
|
|
|
|
RtlZeroMemory(queryTable, 2*sizeof(RTL_QUERY_REGISTRY_TABLE));
|
|
queryTable[0].QueryRoutine = CheckForNoDriveLetterEntry;
|
|
queryTable[0].Flags = RTL_QUERY_REGISTRY_REQUIRED;
|
|
queryTable[0].EntryContext = &hasNoDriveLetterEntry;
|
|
|
|
hasNoDriveLetterEntry = FALSE;
|
|
RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE, MOUNTED_DEVICES_KEY, queryTable, UniqueId, NULL);
|
|
return hasNoDriveLetterEntry;
|
|
}
|
|
|
|
|
|
typedef struct _CHANGE_NOTIFY_WORK_ITEM {
|
|
WORK_QUEUE_ITEM WorkItem;
|
|
PDEVICE_EXTENSION Extension;
|
|
CCHAR StackSize;
|
|
PIRP Irp;
|
|
UNICODE_STRING DeviceName;
|
|
PVOID SystemBuffer;
|
|
ULONG OutputSize;
|
|
} CHANGE_NOTIFY_WORK_ITEM, *PCHANGE_NOTIFY_WORK_ITEM;
|
|
|
|
|
|
VOID IssueUniqueIdChangeNotifyWorker(
|
|
IN PCHANGE_NOTIFY_WORK_ITEM WorkItem,
|
|
IN PMOUNTDEV_UNIQUE_ID UniqueId
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
This routine issues a change notify request to the given mounted device.
|
|
Arguments:
|
|
WorkItem - Supplies the work item.
|
|
UniqueId - Supplies the unique id.
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
PFILE_OBJECT fileObject;
|
|
PDEVICE_OBJECT deviceObject;
|
|
PIRP irp;
|
|
ULONG inputSize;
|
|
PIO_STACK_LOCATION irpSp;
|
|
|
|
status = IoGetDeviceObjectPointer(&WorkItem->DeviceName, FILE_READ_ATTRIBUTES, &fileObject, &deviceObject);
|
|
if (!NT_SUCCESS(status)) {
|
|
IoFreeIrp(WorkItem->Irp);
|
|
ExFreePool(WorkItem->DeviceName.Buffer);
|
|
ExFreePool(WorkItem->SystemBuffer);
|
|
ExFreePool(WorkItem);
|
|
return;
|
|
}
|
|
deviceObject = IoGetAttachedDeviceReference(fileObject->DeviceObject);
|
|
ObDereferenceObject(fileObject);
|
|
|
|
irp = WorkItem->Irp;
|
|
IoInitializeIrp(irp, IoSizeOfIrp(WorkItem->StackSize), WorkItem->StackSize);
|
|
|
|
irp->AssociatedIrp.SystemBuffer = WorkItem->SystemBuffer;
|
|
irp->Tail.Overlay.Thread = PsGetCurrentThread();
|
|
|
|
inputSize = FIELD_OFFSET(MOUNTDEV_UNIQUE_ID, UniqueId) + UniqueId->UniqueIdLength;
|
|
|
|
RtlCopyMemory(irp->AssociatedIrp.SystemBuffer, UniqueId, inputSize);
|
|
|
|
irpSp = IoGetNextIrpStackLocation(irp);
|
|
irpSp->Parameters.DeviceIoControl.InputBufferLength = inputSize;
|
|
irpSp->Parameters.DeviceIoControl.OutputBufferLength = WorkItem->OutputSize;
|
|
irpSp->Parameters.DeviceIoControl.IoControlCode = IOCTL_MOUNTDEV_UNIQUE_ID_CHANGE_NOTIFY;
|
|
irpSp->Parameters.DeviceIoControl.Type3InputBuffer = NULL;
|
|
irpSp->MajorFunction = IRP_MJ_DEVICE_CONTROL;
|
|
irpSp->DeviceObject = deviceObject;
|
|
|
|
IoSetCompletionRoutine(irp, UniqueIdChangeNotifyCompletion, WorkItem, TRUE, TRUE, TRUE);
|
|
IoCallDriver(deviceObject, irp);
|
|
ObDereferenceObject(deviceObject);
|
|
}
|
|
|
|
|
|
VOID UniqueIdChangeNotifyWorker(IN PVOID WorkItem)
|
|
/*++
|
|
Routine Description:
|
|
This routine updates the unique id in the database with the new version.
|
|
Arguments:
|
|
WorkItem - Supplies the work item.
|
|
--*/
|
|
{
|
|
PCHANGE_NOTIFY_WORK_ITEM workItem = WorkItem;
|
|
PMOUNTDEV_UNIQUE_ID_CHANGE_NOTIFY_OUTPUT output;
|
|
PMOUNTDEV_UNIQUE_ID oldUniqueId, newUniqueId;
|
|
|
|
output = workItem->Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
oldUniqueId = ExAllocatePool(PagedPool, sizeof(MOUNTDEV_UNIQUE_ID) + output->OldUniqueIdLength);
|
|
if (!oldUniqueId) {
|
|
ExFreePool(output);
|
|
IoFreeIrp(workItem->Irp);
|
|
ExFreePool(workItem->DeviceName.Buffer);
|
|
ExFreePool(workItem);
|
|
return;
|
|
}
|
|
|
|
oldUniqueId->UniqueIdLength = output->OldUniqueIdLength;
|
|
RtlCopyMemory(oldUniqueId->UniqueId, (PCHAR) output + output->OldUniqueIdOffset, oldUniqueId->UniqueIdLength);
|
|
newUniqueId = ExAllocatePool(PagedPool, sizeof(MOUNTDEV_UNIQUE_ID) + output->NewUniqueIdLength);
|
|
if (!newUniqueId) {
|
|
ExFreePool(oldUniqueId);
|
|
ExFreePool(output);
|
|
IoFreeIrp(workItem->Irp);
|
|
ExFreePool(workItem->DeviceName.Buffer);
|
|
ExFreePool(workItem);
|
|
return;
|
|
}
|
|
|
|
newUniqueId->UniqueIdLength = output->NewUniqueIdLength;
|
|
RtlCopyMemory(newUniqueId->UniqueId, (PCHAR) output + output->NewUniqueIdOffset, newUniqueId->UniqueIdLength);
|
|
MountMgrUniqueIdChangeRoutine(workItem->Extension, oldUniqueId, newUniqueId);
|
|
IssueUniqueIdChangeNotifyWorker(workItem, newUniqueId);
|
|
|
|
ExFreePool(newUniqueId);
|
|
ExFreePool(oldUniqueId);
|
|
}
|
|
|
|
|
|
VOID
|
|
IssueUniqueIdChangeNotify(
|
|
IN PDEVICE_EXTENSION Extension,
|
|
IN PUNICODE_STRING DeviceName,
|
|
IN PMOUNTDEV_UNIQUE_ID UniqueId
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
This routine issues a change notify request to the given mounted device.
|
|
Arguments:
|
|
Extension - Supplies the device extension.
|
|
DeviceName - Supplies a name for the device.
|
|
UniqueId - Supplies the unique id.
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
PFILE_OBJECT fileObject;
|
|
PDEVICE_OBJECT deviceObject;
|
|
PCHANGE_NOTIFY_WORK_ITEM workItem;
|
|
ULONG outputSize;
|
|
PVOID output;
|
|
PIRP irp;
|
|
PIO_STACK_LOCATION irpSp;
|
|
|
|
status = IoGetDeviceObjectPointer(DeviceName, FILE_READ_ATTRIBUTES, &fileObject, &deviceObject);
|
|
if (!NT_SUCCESS(status)) {
|
|
return;
|
|
}
|
|
deviceObject = IoGetAttachedDeviceReference(fileObject->DeviceObject);
|
|
ObDereferenceObject(fileObject);
|
|
|
|
workItem = ExAllocatePool(NonPagedPool, sizeof(CHANGE_NOTIFY_WORK_ITEM));
|
|
if (!workItem) {
|
|
ObDereferenceObject(deviceObject);
|
|
return;
|
|
}
|
|
|
|
workItem->Extension = Extension;
|
|
workItem->StackSize = deviceObject->StackSize;
|
|
workItem->Irp = IoAllocateIrp(deviceObject->StackSize, FALSE);
|
|
ObDereferenceObject(deviceObject);
|
|
if (!workItem->Irp) {
|
|
ExFreePool(workItem);
|
|
return;
|
|
}
|
|
|
|
outputSize = sizeof(MOUNTDEV_UNIQUE_ID_CHANGE_NOTIFY_OUTPUT) + 1024;
|
|
output = ExAllocatePool(NonPagedPool, outputSize);
|
|
if (!output) {
|
|
IoFreeIrp(workItem->Irp);
|
|
ExFreePool(workItem);
|
|
return;
|
|
}
|
|
|
|
workItem->DeviceName.Length = DeviceName->Length;
|
|
workItem->DeviceName.MaximumLength = workItem->DeviceName.Length + sizeof(WCHAR);
|
|
workItem->DeviceName.Buffer = ExAllocatePool(NonPagedPool, workItem->DeviceName.MaximumLength);
|
|
if (!workItem->DeviceName.Buffer) {
|
|
ExFreePool(output);
|
|
IoFreeIrp(workItem->Irp);
|
|
ExFreePool(workItem);
|
|
return;
|
|
}
|
|
|
|
RtlCopyMemory(workItem->DeviceName.Buffer, DeviceName->Buffer, DeviceName->Length);
|
|
workItem->DeviceName.Buffer[DeviceName->Length/sizeof(WCHAR)] = 0;
|
|
|
|
workItem->SystemBuffer = output;
|
|
workItem->OutputSize = outputSize;
|
|
|
|
IssueUniqueIdChangeNotifyWorker(workItem, UniqueId);
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
QueryVolumeName(
|
|
IN HANDLE Handle,
|
|
IN PLONGLONG FileReference,
|
|
IN OUT PUNICODE_STRING VolumeName
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
This routine returns the volume name contained in the reparse point at FileReference.
|
|
Arguments:
|
|
Handle - Supplies a handle to the volume containing the file reference.
|
|
FileReference - Supplies the file reference.
|
|
VolumeName - Returns the volume name.
|
|
Return Value:
|
|
FALSE - Failure.
|
|
TRUE - Success.
|
|
--*/
|
|
{
|
|
UNICODE_STRING fileId;
|
|
OBJECT_ATTRIBUTES oa;
|
|
NTSTATUS status;
|
|
HANDLE h;
|
|
PREPARSE_DATA_BUFFER reparse;
|
|
IO_STATUS_BLOCK ioStatus;
|
|
|
|
fileId.Length = sizeof(LONGLONG);
|
|
fileId.MaximumLength = fileId.Length;
|
|
fileId.Buffer = (PWSTR) FileReference;
|
|
|
|
InitializeObjectAttributes(&oa, &fileId, 0, Handle, NULL);
|
|
|
|
status = ZwOpenFile(&h, FILE_GENERIC_READ, &oa, &ioStatus, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_OPEN_BY_FILE_ID | FILE_OPEN_REPARSE_POINT);
|
|
if (!NT_SUCCESS(status)) {
|
|
return FALSE;
|
|
}
|
|
|
|
reparse = ExAllocatePool(PagedPool, MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
|
|
if (!reparse) {
|
|
ZwClose(h);
|
|
return FALSE;
|
|
}
|
|
|
|
status = ZwFsControlFile(h, NULL, NULL, NULL, &ioStatus, FSCTL_GET_REPARSE_POINT, NULL, 0, reparse, MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
|
|
ZwClose(h);
|
|
if (!NT_SUCCESS(status)) {
|
|
ExFreePool(reparse);
|
|
return FALSE;
|
|
}
|
|
|
|
if (reparse->MountPointReparseBuffer.SubstituteNameLength + sizeof(WCHAR) > VolumeName->MaximumLength) {
|
|
ExFreePool(reparse);
|
|
return FALSE;
|
|
}
|
|
|
|
VolumeName->Length = reparse->MountPointReparseBuffer.SubstituteNameLength;
|
|
RtlCopyMemory(VolumeName->Buffer, (PCHAR) reparse->MountPointReparseBuffer.PathBuffer + reparse->MountPointReparseBuffer.SubstituteNameOffset, VolumeName->Length);
|
|
ExFreePool(reparse);
|
|
if (VolumeName->Buffer[VolumeName->Length/sizeof(WCHAR) - 1] != '\\') {
|
|
return FALSE;
|
|
}
|
|
|
|
VolumeName->Length -= sizeof(WCHAR);
|
|
VolumeName->Buffer[VolumeName->Length/sizeof(WCHAR)] = 0;
|
|
|
|
if (!MOUNTMGR_IS_VOLUME_NAME(VolumeName)) {
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
QueryUniqueIdQueryRoutine(
|
|
IN PWSTR ValueName,
|
|
IN ULONG ValueType,
|
|
IN PVOID ValueData,
|
|
IN ULONG ValueLength,
|
|
IN PVOID Context,
|
|
IN PVOID EntryContext
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
This routine queries the unique id for the given value.
|
|
Arguments:
|
|
ValueName - Supplies the name of the registry value.
|
|
ValueType - Supplies the type of the registry value.
|
|
ValueData - Supplies the data of the registry value.
|
|
ValueLength - Supplies the length of the registry value.
|
|
Context - Returns the unique id.
|
|
EntryContext - Not used.
|
|
Return Value:
|
|
NTSTATUS
|
|
--*/
|
|
{
|
|
PMOUNTDEV_UNIQUE_ID uniqueId;
|
|
|
|
if (ValueLength >= 0x10000) {
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
uniqueId = ExAllocatePool(PagedPool, sizeof(MOUNTDEV_UNIQUE_ID) + ValueLength);
|
|
if (!uniqueId) {
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
uniqueId->UniqueIdLength = (USHORT) ValueLength;
|
|
RtlCopyMemory(uniqueId->UniqueId, ValueData, ValueLength);
|
|
|
|
*((PMOUNTDEV_UNIQUE_ID*) Context) = uniqueId;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
QueryUniqueIdFromMaster(
|
|
IN PUNICODE_STRING VolumeName,
|
|
OUT PMOUNTDEV_UNIQUE_ID* UniqueId
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
This routine queries the unique id from the master database.
|
|
Arguments:
|
|
VolumeName - Supplies the volume name.
|
|
UniqueId - Returns the unique id.
|
|
Return Value:
|
|
NTSTATUS
|
|
--*/
|
|
{
|
|
RTL_QUERY_REGISTRY_TABLE queryTable[2];
|
|
|
|
RtlZeroMemory(queryTable, 2*sizeof(RTL_QUERY_REGISTRY_TABLE));
|
|
queryTable[0].QueryRoutine = QueryUniqueIdQueryRoutine;
|
|
queryTable[0].Flags = RTL_QUERY_REGISTRY_REQUIRED;
|
|
queryTable[0].Name = VolumeName->Buffer;
|
|
|
|
*UniqueId = NULL;
|
|
RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE, MOUNTED_DEVICES_KEY, queryTable, UniqueId, NULL);
|
|
if (!(*UniqueId)) {
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
DeleteDriveLetterRoutine(
|
|
IN PWSTR ValueName,
|
|
IN ULONG ValueType,
|
|
IN PVOID ValueData,
|
|
IN ULONG ValueLength,
|
|
IN PVOID Context,
|
|
IN PVOID EntryContext
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
This routine deletes the "no drive letter" entry.
|
|
Arguments:
|
|
ValueName - Supplies the name of the registry value.
|
|
ValueType - Supplies the type of the registry value.
|
|
ValueData - Supplies the data of the registry value.
|
|
ValueLength - Supplies the length of the registry value.
|
|
Context - Supplies the unique id.
|
|
EntryContext - Not used.
|
|
Return Value:
|
|
NTSTATUS
|
|
--*/
|
|
{
|
|
PMOUNTDEV_UNIQUE_ID uniqueId = Context;
|
|
UNICODE_STRING string;
|
|
|
|
if (ValueType != REG_BINARY || ValueLength != uniqueId->UniqueIdLength || RtlCompareMemory(uniqueId->UniqueId, ValueData, ValueLength) != ValueLength) {
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
RtlInitUnicodeString(&string, ValueName);
|
|
if (IsDriveLetter(&string)) {
|
|
RtlDeleteRegistryValue(RTL_REGISTRY_ABSOLUTE, MOUNTED_DEVICES_KEY, ValueName);
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
VOID DeleteRegistryDriveLetter(IN PMOUNTDEV_UNIQUE_ID UniqueId)
|
|
/*++
|
|
Routine Description:
|
|
This routine checks the current database to see if the given unique id already has a drive letter.
|
|
Arguments:
|
|
UniqueId - Supplies the unique id.
|
|
Return Value:
|
|
FALSE - The given unique id does not already have a drive letter.
|
|
TRUE - The given unique id already has a drive letter.
|
|
--*/
|
|
{
|
|
RTL_QUERY_REGISTRY_TABLE queryTable[2];
|
|
|
|
RtlZeroMemory(queryTable, 2*sizeof(RTL_QUERY_REGISTRY_TABLE));
|
|
queryTable[0].QueryRoutine = DeleteDriveLetterRoutine;
|
|
queryTable[0].Flags = RTL_QUERY_REGISTRY_REQUIRED;
|
|
|
|
RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE, MOUNTED_DEVICES_KEY, queryTable, UniqueId, NULL);
|
|
}
|
|
|
|
|
|
BOOLEAN HasDriveLetter(IN PMOUNTED_DEVICE_INFORMATION DeviceInfo)
|
|
/*++
|
|
Routine Description:
|
|
This routine computes whether or not the given device has a drive letter.
|
|
Arguments:
|
|
DeviceInfo - Supplies the device information.
|
|
Return Value:
|
|
FALSE - This device does not have a drive letter.
|
|
TRUE - This device does have a drive letter.
|
|
--*/
|
|
{
|
|
PLIST_ENTRY l;
|
|
PSYMBOLIC_LINK_NAME_ENTRY symEntry;
|
|
|
|
for (l = DeviceInfo->SymbolicLinkNames.Flink; l != &DeviceInfo->SymbolicLinkNames; l = l->Flink) {
|
|
symEntry = CONTAINING_RECORD(l, SYMBOLIC_LINK_NAME_ENTRY, ListEntry);
|
|
if (symEntry->IsInDatabase && IsDriveLetter(&symEntry->SymbolicLinkName)) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
DeleteNoDriveLetterEntryRoutine(
|
|
IN PWSTR ValueName,
|
|
IN ULONG ValueType,
|
|
IN PVOID ValueData,
|
|
IN ULONG ValueLength,
|
|
IN PVOID Context,
|
|
IN PVOID EntryContext
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
This routine deletes the "no drive letter" entry.
|
|
Arguments:
|
|
ValueName - Supplies the name of the registry value.
|
|
ValueType - Supplies the type of the registry value.
|
|
ValueData - Supplies the data of the registry value.
|
|
ValueLength - Supplies the length of the registry value.
|
|
Context - Supplies the unique id.
|
|
EntryContext - Not used.
|
|
Return Value:
|
|
NTSTATUS
|
|
--*/
|
|
{
|
|
PMOUNTDEV_UNIQUE_ID uniqueId = Context;
|
|
|
|
if (ValueName[0] != '#' || ValueType != REG_BINARY || ValueLength != uniqueId->UniqueIdLength || RtlCompareMemory(uniqueId->UniqueId, ValueData, ValueLength) != ValueLength) {
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
RtlDeleteRegistryValue(RTL_REGISTRY_ABSOLUTE, MOUNTED_DEVICES_KEY, ValueName);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
VOID DeleteNoDriveLetterEntry(IN PMOUNTDEV_UNIQUE_ID UniqueId)
|
|
/*++
|
|
Routine Description:
|
|
This routine deletes the "no drive letter" entry for the given device.
|
|
Arguments:
|
|
UniqueId - Supplies the unique id.
|
|
--*/
|
|
{
|
|
RTL_QUERY_REGISTRY_TABLE queryTable[2];
|
|
|
|
RtlZeroMemory(queryTable, 2*sizeof(RTL_QUERY_REGISTRY_TABLE));
|
|
queryTable[0].QueryRoutine = DeleteNoDriveLetterEntryRoutine;
|
|
queryTable[0].Flags = RTL_QUERY_REGISTRY_REQUIRED;
|
|
|
|
RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE, MOUNTED_DEVICES_KEY, queryTable, UniqueId, NULL);
|
|
}
|
|
|
|
|
|
VOID
|
|
MountMgrNotifyNameChange(
|
|
IN PDEVICE_EXTENSION Extension,
|
|
IN PUNICODE_STRING DeviceName,
|
|
IN BOOLEAN CheckForPdo
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
This routine performs a target notification on 'DeviceName' to alert of a name change on the device.
|
|
Arguments:
|
|
Extension - Supplies the device extension.
|
|
DeviceName - Supplies the device name.
|
|
CheckForPdo - Supplies whether or not there needs to be a check for PDO status.
|
|
--*/
|
|
{
|
|
PLIST_ENTRY l;
|
|
PMOUNTED_DEVICE_INFORMATION deviceInfo;
|
|
NTSTATUS status;
|
|
PFILE_OBJECT fileObject;
|
|
PDEVICE_OBJECT deviceObject;
|
|
KEVENT event;
|
|
PIRP irp;
|
|
IO_STATUS_BLOCK ioStatus;
|
|
PIO_STACK_LOCATION irpSp;
|
|
PDEVICE_RELATIONS deviceRelations;
|
|
TARGET_DEVICE_CUSTOM_NOTIFICATION notification;
|
|
|
|
if (CheckForPdo) {
|
|
for (l = Extension->MountedDeviceList.Flink; l != &Extension->MountedDeviceList; l = l->Flink) {
|
|
deviceInfo = CONTAINING_RECORD(l, MOUNTED_DEVICE_INFORMATION, ListEntry);
|
|
if (!RtlCompareUnicodeString(DeviceName, &deviceInfo->DeviceName, TRUE)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (l == &Extension->MountedDeviceList || deviceInfo->NotAPdo) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
status = IoGetDeviceObjectPointer(DeviceName, FILE_READ_ATTRIBUTES, &fileObject, &deviceObject);
|
|
if (!NT_SUCCESS(status)) {
|
|
return;
|
|
}
|
|
deviceObject = IoGetAttachedDeviceReference(fileObject->DeviceObject);
|
|
|
|
KeInitializeEvent(&event, NotificationEvent, FALSE);
|
|
|
|
irp = IoBuildDeviceIoControlRequest(0, deviceObject, NULL, 0, NULL, 0, FALSE, &event, &ioStatus);
|
|
if (!irp) {
|
|
ObDereferenceObject(deviceObject);
|
|
ObDereferenceObject(fileObject);
|
|
return;
|
|
}
|
|
|
|
irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
|
|
irp->IoStatus.Information = 0;
|
|
irpSp = IoGetNextIrpStackLocation(irp);
|
|
irpSp->MajorFunction = IRP_MJ_PNP;
|
|
irpSp->MinorFunction = IRP_MN_QUERY_DEVICE_RELATIONS;
|
|
irpSp->Parameters.QueryDeviceRelations.Type = TargetDeviceRelation;
|
|
irpSp->FileObject = fileObject;
|
|
|
|
status = IoCallDriver(deviceObject, irp);
|
|
if (status == STATUS_PENDING) {
|
|
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
|
|
status = ioStatus.Status;
|
|
}
|
|
|
|
ObDereferenceObject(deviceObject);
|
|
ObDereferenceObject(fileObject);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
return;
|
|
}
|
|
|
|
deviceRelations = (PDEVICE_RELATIONS) ioStatus.Information;
|
|
if (deviceRelations->Count < 1) {
|
|
ExFreePool(deviceRelations);
|
|
return;
|
|
}
|
|
|
|
deviceObject = deviceRelations->Objects[0];
|
|
ExFreePool(deviceRelations);
|
|
|
|
notification.Version = 1;
|
|
notification.Size = (USHORT) FIELD_OFFSET(TARGET_DEVICE_CUSTOM_NOTIFICATION, CustomDataBuffer);
|
|
RtlCopyMemory(¬ification.Event, &GUID_IO_VOLUME_NAME_CHANGE, sizeof(GUID_IO_VOLUME_NAME_CHANGE));
|
|
notification.FileObject = NULL;
|
|
notification.NameBufferOffset = -1;
|
|
|
|
IoReportTargetDeviceChangeAsynchronous(deviceObject, ¬ification, NULL, NULL);
|
|
ObDereferenceObject(deviceObject);
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
MountMgrCreatePointWorker(
|
|
IN PDEVICE_EXTENSION Extension,
|
|
IN PUNICODE_STRING SymbolicLinkName,
|
|
IN PUNICODE_STRING DeviceName
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
This routine creates a mount point.
|
|
Arguments:
|
|
Extension - Supplies the device extension.
|
|
SymbolicLinkName - Supplies the symbolic link name.
|
|
DeviceName - Supplies the device name.
|
|
Return Value:
|
|
NTSTATUS
|
|
--*/
|
|
{
|
|
UNICODE_STRING symbolicLinkName, deviceName;
|
|
NTSTATUS status;
|
|
UNICODE_STRING targetName;
|
|
PMOUNTDEV_UNIQUE_ID uniqueId;
|
|
PWSTR symName;
|
|
PLIST_ENTRY l;
|
|
PMOUNTED_DEVICE_INFORMATION deviceInfo;
|
|
PSYMBOLIC_LINK_NAME_ENTRY symlinkEntry;
|
|
|
|
symbolicLinkName = *SymbolicLinkName;
|
|
deviceName = *DeviceName;
|
|
|
|
status = QueryDeviceInformation(&deviceName, &targetName, NULL, NULL, NULL);
|
|
if (!NT_SUCCESS(status)) {
|
|
return status;
|
|
}
|
|
|
|
for (l = Extension->MountedDeviceList.Flink; l != &Extension->MountedDeviceList; l = l->Flink) {
|
|
deviceInfo = CONTAINING_RECORD(l, MOUNTED_DEVICE_INFORMATION, ListEntry);
|
|
if (!RtlCompareUnicodeString(&targetName, &deviceInfo->DeviceName, TRUE)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
symName = ExAllocatePool(PagedPool, symbolicLinkName.Length + sizeof(WCHAR));
|
|
if (!symName) {
|
|
ExFreePool(targetName.Buffer);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
RtlCopyMemory(symName, symbolicLinkName.Buffer, symbolicLinkName.Length);
|
|
symName[symbolicLinkName.Length/sizeof(WCHAR)] = 0;
|
|
|
|
symbolicLinkName.Buffer = symName;
|
|
symbolicLinkName.MaximumLength += sizeof(WCHAR);
|
|
|
|
if (l == &Extension->MountedDeviceList) {
|
|
status = QueryDeviceInformation(&deviceName, NULL, &uniqueId, NULL, NULL);
|
|
if (!NT_SUCCESS(status)) {
|
|
ExFreePool(symName);
|
|
ExFreePool(targetName.Buffer);
|
|
return status;
|
|
}
|
|
|
|
status = IoCreateSymbolicLink(&symbolicLinkName, &targetName);
|
|
if (!NT_SUCCESS(status)) {
|
|
ExFreePool(uniqueId);
|
|
ExFreePool(symName);
|
|
ExFreePool(targetName.Buffer);
|
|
return status;
|
|
}
|
|
|
|
if (IsDriveLetter(&symbolicLinkName)) {
|
|
DeleteRegistryDriveLetter(uniqueId);
|
|
}
|
|
|
|
status = RtlWriteRegistryValue(RTL_REGISTRY_ABSOLUTE, MOUNTED_DEVICES_KEY, symName, REG_BINARY, uniqueId->UniqueId, uniqueId->UniqueIdLength);
|
|
ExFreePool(uniqueId);
|
|
ExFreePool(symName);
|
|
ExFreePool(targetName.Buffer);
|
|
return status;
|
|
}
|
|
|
|
if (IsDriveLetter(&symbolicLinkName) && HasDriveLetter(deviceInfo)) {
|
|
ExFreePool(symName);
|
|
ExFreePool(targetName.Buffer);
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
status = IoCreateSymbolicLink(&symbolicLinkName, &targetName);
|
|
ExFreePool(targetName.Buffer);
|
|
if (!NT_SUCCESS(status)) {
|
|
ExFreePool(symName);
|
|
return status;
|
|
}
|
|
|
|
uniqueId = deviceInfo->UniqueId;
|
|
status = RtlWriteRegistryValue(RTL_REGISTRY_ABSOLUTE, MOUNTED_DEVICES_KEY, symName, REG_BINARY, uniqueId->UniqueId, uniqueId->UniqueIdLength);
|
|
if (!NT_SUCCESS(status)) {
|
|
IoDeleteSymbolicLink(&symbolicLinkName);
|
|
ExFreePool(symName);
|
|
return status;
|
|
}
|
|
|
|
symlinkEntry = ExAllocatePool(PagedPool, sizeof(SYMBOLIC_LINK_NAME_ENTRY));
|
|
if (!symlinkEntry) {
|
|
IoDeleteSymbolicLink(&symbolicLinkName);
|
|
ExFreePool(symName);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
symlinkEntry->SymbolicLinkName.Length = symbolicLinkName.Length;
|
|
symlinkEntry->SymbolicLinkName.MaximumLength = symlinkEntry->SymbolicLinkName.Length + sizeof(WCHAR);
|
|
symlinkEntry->SymbolicLinkName.Buffer = ExAllocatePool(PagedPool, symlinkEntry->SymbolicLinkName.MaximumLength);
|
|
if (!symlinkEntry->SymbolicLinkName.Buffer) {
|
|
ExFreePool(symlinkEntry);
|
|
IoDeleteSymbolicLink(&symbolicLinkName);
|
|
ExFreePool(symName);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
RtlCopyMemory(symlinkEntry->SymbolicLinkName.Buffer, symbolicLinkName.Buffer, symbolicLinkName.Length);
|
|
symlinkEntry->SymbolicLinkName.Buffer[symlinkEntry->SymbolicLinkName.Length/sizeof(WCHAR)] = 0;
|
|
symlinkEntry->IsInDatabase = TRUE;
|
|
|
|
InsertTailList(&deviceInfo->SymbolicLinkNames, &symlinkEntry->ListEntry);
|
|
|
|
SendLinkCreated(&symlinkEntry->SymbolicLinkName);
|
|
|
|
if (IsDriveLetter(&symbolicLinkName)) {
|
|
DeleteNoDriveLetterEntry(uniqueId);
|
|
}
|
|
|
|
ExFreePool(symName);
|
|
|
|
MountMgrNotify(Extension);
|
|
|
|
if (!deviceInfo->NotAPdo) {
|
|
MountMgrNotifyNameChange(Extension, DeviceName, FALSE);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
WriteUniqueIdToMaster(
|
|
IN PDEVICE_EXTENSION Extension,
|
|
IN PMOUNTMGR_FILE_ENTRY DatabaseEntry
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
This routine writes the unique id to the master database.
|
|
Arguments:
|
|
Extension - Supplies the device extension.
|
|
DatabaseEntry - Supplies the database entry.
|
|
DeviceName - Supplies the device name.
|
|
Return Value:
|
|
NTSTATUS
|
|
--*/
|
|
{
|
|
PWSTR name;
|
|
NTSTATUS status;
|
|
UNICODE_STRING symName;
|
|
PLIST_ENTRY l;
|
|
PMOUNTED_DEVICE_INFORMATION deviceInfo;
|
|
|
|
name = ExAllocatePool(PagedPool, DatabaseEntry->VolumeNameLength + sizeof(WCHAR));
|
|
if (!name) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
RtlCopyMemory(name, (PCHAR) DatabaseEntry + DatabaseEntry->VolumeNameOffset, DatabaseEntry->VolumeNameLength);
|
|
name[DatabaseEntry->VolumeNameLength/sizeof(WCHAR)] = 0;
|
|
|
|
status = RtlWriteRegistryValue(RTL_REGISTRY_ABSOLUTE, MOUNTED_DEVICES_KEY, name, REG_BINARY, (PCHAR) DatabaseEntry + DatabaseEntry->UniqueIdOffset, DatabaseEntry->UniqueIdLength);
|
|
ExFreePool(name);
|
|
symName.Length = symName.MaximumLength = DatabaseEntry->VolumeNameLength;
|
|
symName.Buffer = (PWSTR) ((PCHAR) DatabaseEntry + DatabaseEntry->VolumeNameOffset);
|
|
for (l = Extension->MountedDeviceList.Flink; l != &Extension->MountedDeviceList; l = l->Flink) {
|
|
deviceInfo = CONTAINING_RECORD(l, MOUNTED_DEVICE_INFORMATION, ListEntry);
|
|
if (DatabaseEntry->UniqueIdLength == deviceInfo->UniqueId->UniqueIdLength &&
|
|
RtlCompareMemory((PCHAR) DatabaseEntry + DatabaseEntry->UniqueIdOffset, deviceInfo->UniqueId->UniqueId, DatabaseEntry->UniqueIdLength) == DatabaseEntry->UniqueIdLength) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (l != &Extension->MountedDeviceList) {
|
|
MountMgrCreatePointWorker(Extension, &symName, &deviceInfo->DeviceName);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
VOID
|
|
UpdateReplicatedUniqueIds(
|
|
IN PMOUNTED_DEVICE_INFORMATION DeviceInfo,
|
|
IN PMOUNTMGR_FILE_ENTRY DatabaseEntry
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
This routine updates the list of replicated unique ids in the device info.
|
|
Arguments:
|
|
DeviceInfo - Supplies the device information.
|
|
DatabaseEntry - Supplies the database entry.
|
|
--*/
|
|
{
|
|
PLIST_ENTRY l;
|
|
PREPLICATED_UNIQUE_ID replUniqueId;
|
|
|
|
for (l = DeviceInfo->ReplicatedUniqueIds.Flink; l != &DeviceInfo->ReplicatedUniqueIds; l = l->Flink) {
|
|
replUniqueId = CONTAINING_RECORD(l, REPLICATED_UNIQUE_ID, ListEntry);
|
|
if (replUniqueId->UniqueId->UniqueIdLength == DatabaseEntry->UniqueIdLength &&
|
|
RtlCompareMemory(replUniqueId->UniqueId->UniqueId, (PCHAR) DatabaseEntry + DatabaseEntry->UniqueIdOffset, replUniqueId->UniqueId->UniqueIdLength) == replUniqueId->UniqueId->UniqueIdLength) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (l != &DeviceInfo->ReplicatedUniqueIds) {
|
|
return;
|
|
}
|
|
|
|
replUniqueId = ExAllocatePool(PagedPool, sizeof(REPLICATED_UNIQUE_ID));
|
|
if (!replUniqueId) {
|
|
return;
|
|
}
|
|
|
|
replUniqueId->UniqueId = ExAllocatePool(PagedPool, sizeof(MOUNTDEV_UNIQUE_ID) + DatabaseEntry->UniqueIdLength);
|
|
if (!replUniqueId->UniqueId) {
|
|
ExFreePool(replUniqueId);
|
|
return;
|
|
}
|
|
|
|
replUniqueId->UniqueId->UniqueIdLength = DatabaseEntry->UniqueIdLength;
|
|
RtlCopyMemory(replUniqueId->UniqueId->UniqueId, (PCHAR) DatabaseEntry + DatabaseEntry->UniqueIdOffset, replUniqueId->UniqueId->UniqueIdLength);
|
|
InsertTailList(&DeviceInfo->ReplicatedUniqueIds, &replUniqueId->ListEntry);
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
IsUniqueIdPresent(
|
|
IN PDEVICE_EXTENSION Extension,
|
|
IN PMOUNTMGR_FILE_ENTRY DatabaseEntry
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
This routine checks to see if the given unique id exists in the system.
|
|
Arguments:
|
|
Extension - Supplies the device extension.
|
|
DatabaseEntry - Supplies the database entry.
|
|
Return Value:
|
|
FALSE - The unique id is not in the system.
|
|
TRUE - The unique id is in the system.
|
|
--*/
|
|
{
|
|
PLIST_ENTRY l;
|
|
PMOUNTED_DEVICE_INFORMATION deviceInfo;
|
|
|
|
for (l = Extension->MountedDeviceList.Flink; l != &Extension->MountedDeviceList; l = l->Flink) {
|
|
deviceInfo = CONTAINING_RECORD(l, MOUNTED_DEVICE_INFORMATION, ListEntry);
|
|
if (DatabaseEntry->UniqueIdLength == deviceInfo->UniqueId->UniqueIdLength &&
|
|
RtlCompareMemory((PCHAR) DatabaseEntry + DatabaseEntry->UniqueIdOffset, deviceInfo->UniqueId->UniqueId, DatabaseEntry->UniqueIdLength) == DatabaseEntry->UniqueIdLength) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
VOID ReconcileThisDatabaseWithMasterWorker(IN PVOID WorkItem)
|
|
/*++
|
|
Routine Description:
|
|
This routine reconciles the remote database with the master database.
|
|
Arguments:
|
|
WorkItem - Supplies the device information.
|
|
--*/
|
|
{
|
|
PRECONCILE_WORK_ITEM_INFO workItem = WorkItem;
|
|
PDEVICE_EXTENSION Extension;
|
|
PMOUNTED_DEVICE_INFORMATION DeviceInfo;
|
|
PLIST_ENTRY l;
|
|
PMOUNTED_DEVICE_INFORMATION deviceInfo;
|
|
HANDLE remoteDatabaseHandle, indexHandle, junctionHandle;
|
|
UNICODE_STRING suffix, indexName;
|
|
OBJECT_ATTRIBUTES oa;
|
|
NTSTATUS status;
|
|
IO_STATUS_BLOCK ioStatus;
|
|
FILE_REPARSE_POINT_INFORMATION reparseInfo, previousReparseInfo;
|
|
ULONG offset;
|
|
PMOUNTMGR_FILE_ENTRY entry;
|
|
WCHAR volumeNameBuffer[MAX_VOLUME_PATH];
|
|
UNICODE_STRING volumeName, otherVolumeName;
|
|
BOOLEAN restartScan;
|
|
PMOUNTDEV_UNIQUE_ID uniqueId;
|
|
ULONG entryLength;
|
|
|
|
Extension = workItem->Extension;
|
|
DeviceInfo = workItem->DeviceInfo;
|
|
|
|
status = WaitForRemoteDatabaseSemaphore(Extension);
|
|
if (!NT_SUCCESS(status)) {
|
|
ASSERT(FALSE);
|
|
return;
|
|
}
|
|
|
|
KeWaitForSingleObject(&Extension->Mutex, Executive, KernelMode, FALSE, NULL);
|
|
for (l = Extension->MountedDeviceList.Flink; l != &Extension->MountedDeviceList; l = l->Flink) {
|
|
deviceInfo = CONTAINING_RECORD(l, MOUNTED_DEVICE_INFORMATION, ListEntry);
|
|
if (deviceInfo == DeviceInfo) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (l == &Extension->MountedDeviceList) {
|
|
KeReleaseSemaphore(&Extension->Mutex, IO_NO_INCREMENT, 1, FALSE);
|
|
ReleaseRemoteDatabaseSemaphore(Extension);
|
|
return;
|
|
}
|
|
|
|
if (DeviceInfo->IsRemovable) {
|
|
KeReleaseSemaphore(&Extension->Mutex, IO_NO_INCREMENT, 1, FALSE);
|
|
ReleaseRemoteDatabaseSemaphore(Extension);
|
|
return;
|
|
}
|
|
|
|
remoteDatabaseHandle = OpenRemoteDatabase(&DeviceInfo->DeviceName, FALSE);
|
|
|
|
RtlInitUnicodeString(&suffix, L"\\$Extend\\$Reparse:$R:$INDEX_ALLOCATION");
|
|
indexName.Length = DeviceInfo->DeviceName.Length + suffix.Length;
|
|
indexName.MaximumLength = indexName.Length + sizeof(WCHAR);
|
|
indexName.Buffer = ExAllocatePool(PagedPool, indexName.MaximumLength);
|
|
if (!indexName.Buffer) {
|
|
if (remoteDatabaseHandle) {
|
|
CloseRemoteDatabase(remoteDatabaseHandle);
|
|
}
|
|
KeReleaseSemaphore(&Extension->Mutex, IO_NO_INCREMENT, 1, FALSE);
|
|
ReleaseRemoteDatabaseSemaphore(Extension);
|
|
return;
|
|
}
|
|
|
|
RtlCopyMemory(indexName.Buffer, DeviceInfo->DeviceName.Buffer, DeviceInfo->DeviceName.Length);
|
|
RtlCopyMemory((PCHAR) indexName.Buffer + DeviceInfo->DeviceName.Length, suffix.Buffer, suffix.Length);
|
|
indexName.Buffer[indexName.Length/sizeof(WCHAR)] = 0;
|
|
|
|
InitializeObjectAttributes(&oa, &indexName, OBJ_CASE_INSENSITIVE, 0, 0);
|
|
|
|
status = ZwOpenFile(&indexHandle, FILE_GENERIC_READ, &oa, &ioStatus, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_SYNCHRONOUS_IO_ALERT);
|
|
ExFreePool(indexName.Buffer);
|
|
if (!NT_SUCCESS(status)) {
|
|
if (remoteDatabaseHandle) {
|
|
TruncateRemoteDatabase(remoteDatabaseHandle, 0);
|
|
CloseRemoteDatabase(remoteDatabaseHandle);
|
|
}
|
|
KeReleaseSemaphore(&Extension->Mutex, IO_NO_INCREMENT, 1, FALSE);
|
|
ReleaseRemoteDatabaseSemaphore(Extension);
|
|
return;
|
|
}
|
|
|
|
status = ZwQueryDirectoryFile(indexHandle, NULL, NULL, NULL, &ioStatus, &reparseInfo, sizeof(reparseInfo), FileReparsePointInformation, TRUE, NULL, FALSE);
|
|
if (!NT_SUCCESS(status)) {
|
|
ZwClose(indexHandle);
|
|
if (remoteDatabaseHandle) {
|
|
TruncateRemoteDatabase(remoteDatabaseHandle, 0);
|
|
CloseRemoteDatabase(remoteDatabaseHandle);
|
|
}
|
|
KeReleaseSemaphore(&Extension->Mutex, IO_NO_INCREMENT, 1, FALSE);
|
|
ReleaseRemoteDatabaseSemaphore(Extension);
|
|
return;
|
|
}
|
|
|
|
if (!remoteDatabaseHandle) {
|
|
remoteDatabaseHandle = OpenRemoteDatabase(&DeviceInfo->DeviceName, TRUE);
|
|
if (!remoteDatabaseHandle) {
|
|
ZwClose(indexHandle);
|
|
KeReleaseSemaphore(&Extension->Mutex, IO_NO_INCREMENT, 1, FALSE);
|
|
ReleaseRemoteDatabaseSemaphore(Extension);
|
|
return;
|
|
}
|
|
}
|
|
|
|
KeReleaseSemaphore(&Extension->Mutex, IO_NO_INCREMENT, 1, FALSE);
|
|
|
|
offset = 0;
|
|
for (;;) {
|
|
entry = GetRemoteDatabaseEntry(remoteDatabaseHandle, offset);
|
|
if (!entry) {
|
|
break;
|
|
}
|
|
|
|
entry->RefCount = 0;
|
|
status = WriteRemoteDatabaseEntry(remoteDatabaseHandle, offset, entry);
|
|
if (!NT_SUCCESS(status)) {
|
|
ExFreePool(entry);
|
|
ZwClose(indexHandle);
|
|
CloseRemoteDatabase(remoteDatabaseHandle);
|
|
ReleaseRemoteDatabaseSemaphore(Extension);
|
|
return;
|
|
}
|
|
|
|
offset += entry->EntryLength;
|
|
ExFreePool(entry);
|
|
}
|
|
|
|
volumeName.MaximumLength = MAX_VOLUME_PATH*sizeof(WCHAR);
|
|
volumeName.Length = 0;
|
|
volumeName.Buffer = volumeNameBuffer;
|
|
|
|
restartScan = TRUE;
|
|
for (;;) {
|
|
previousReparseInfo = reparseInfo;
|
|
status = ZwQueryDirectoryFile(indexHandle, NULL, NULL, NULL, &ioStatus, &reparseInfo, sizeof(reparseInfo), FileReparsePointInformation, TRUE, NULL, restartScan);
|
|
if (restartScan) {
|
|
restartScan = FALSE;
|
|
} else {
|
|
if (previousReparseInfo.FileReference == reparseInfo.FileReference && previousReparseInfo.Tag == reparseInfo.Tag) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
break;
|
|
}
|
|
|
|
if (reparseInfo.Tag != IO_REPARSE_TAG_MOUNT_POINT) {
|
|
continue;
|
|
}
|
|
|
|
if (!QueryVolumeName(indexHandle, &reparseInfo.FileReference, &volumeName)) {
|
|
continue;
|
|
}
|
|
|
|
offset = 0;
|
|
for (;;) {
|
|
entry = GetRemoteDatabaseEntry(remoteDatabaseHandle, offset);
|
|
if (!entry) {
|
|
break;
|
|
}
|
|
|
|
otherVolumeName.Length = otherVolumeName.MaximumLength = entry->VolumeNameLength;
|
|
otherVolumeName.Buffer = (PWSTR) ((PCHAR) entry + entry->VolumeNameOffset);
|
|
if (RtlEqualUnicodeString(&otherVolumeName, &volumeName, TRUE)) {
|
|
break;
|
|
}
|
|
|
|
offset += entry->EntryLength;
|
|
ExFreePool(entry);
|
|
}
|
|
|
|
if (!entry) {
|
|
KeWaitForSingleObject(&Extension->Mutex, Executive, KernelMode, FALSE, NULL);
|
|
status = QueryUniqueIdFromMaster(&volumeName, &uniqueId);
|
|
KeReleaseSemaphore(&Extension->Mutex, IO_NO_INCREMENT, 1, FALSE);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
continue;
|
|
}
|
|
|
|
entryLength = sizeof(MOUNTMGR_FILE_ENTRY) + volumeName.Length + uniqueId->UniqueIdLength;
|
|
entry = ExAllocatePool(PagedPool, entryLength);
|
|
if (!entry) {
|
|
ExFreePool(uniqueId);
|
|
continue;
|
|
}
|
|
|
|
entry->EntryLength = entryLength;
|
|
entry->RefCount = 1;
|
|
entry->VolumeNameOffset = sizeof(MOUNTMGR_FILE_ENTRY);
|
|
entry->VolumeNameLength = volumeName.Length;
|
|
entry->UniqueIdOffset = entry->VolumeNameOffset + entry->VolumeNameLength;
|
|
entry->UniqueIdLength = uniqueId->UniqueIdLength;
|
|
|
|
RtlCopyMemory((PCHAR) entry + entry->VolumeNameOffset, volumeName.Buffer, entry->VolumeNameLength);
|
|
RtlCopyMemory((PCHAR) entry + entry->UniqueIdOffset, uniqueId->UniqueId, entry->UniqueIdLength);
|
|
|
|
status = AddRemoteDatabaseEntry(remoteDatabaseHandle, entry);
|
|
ExFreePool(entry);
|
|
ExFreePool(uniqueId);
|
|
if (!NT_SUCCESS(status)) {
|
|
ZwClose(indexHandle);
|
|
CloseRemoteDatabase(remoteDatabaseHandle);
|
|
ReleaseRemoteDatabaseSemaphore(Extension);
|
|
return;
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
if (entry->RefCount) {
|
|
entry->RefCount++;
|
|
status = WriteRemoteDatabaseEntry(remoteDatabaseHandle, offset, entry);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
ExFreePool(entry);
|
|
ZwClose(indexHandle);
|
|
CloseRemoteDatabase(remoteDatabaseHandle);
|
|
ReleaseRemoteDatabaseSemaphore(Extension);
|
|
return;
|
|
}
|
|
} else {
|
|
KeWaitForSingleObject(&Extension->Mutex, Executive, KernelMode, FALSE, NULL);
|
|
status = QueryUniqueIdFromMaster(&volumeName, &uniqueId);
|
|
if (NT_SUCCESS(status)) {
|
|
if (uniqueId->UniqueIdLength == entry->UniqueIdLength && RtlCompareMemory(uniqueId->UniqueId, (PCHAR) entry + entry->UniqueIdOffset, entry->UniqueIdLength) == entry->UniqueIdLength) {
|
|
entry->RefCount++;
|
|
status = WriteRemoteDatabaseEntry(remoteDatabaseHandle, offset, entry);
|
|
if (!NT_SUCCESS(status)) {
|
|
ExFreePool(uniqueId);
|
|
KeReleaseSemaphore(&Extension->Mutex, IO_NO_INCREMENT, 1, FALSE);
|
|
ExFreePool(entry);
|
|
ZwClose(indexHandle);
|
|
CloseRemoteDatabase(remoteDatabaseHandle);
|
|
ReleaseRemoteDatabaseSemaphore(Extension);
|
|
return;
|
|
}
|
|
} else if (IsUniqueIdPresent(Extension, entry)) {
|
|
status = WriteUniqueIdToMaster(Extension, entry);
|
|
if (!NT_SUCCESS(status)) {
|
|
ExFreePool(uniqueId);
|
|
KeReleaseSemaphore(&Extension->Mutex, IO_NO_INCREMENT, 1, FALSE);
|
|
ExFreePool(entry);
|
|
ZwClose(indexHandle);
|
|
CloseRemoteDatabase(remoteDatabaseHandle);
|
|
ReleaseRemoteDatabaseSemaphore(Extension);
|
|
return;
|
|
}
|
|
|
|
entry->RefCount++;
|
|
status = WriteRemoteDatabaseEntry(remoteDatabaseHandle, offset, entry);
|
|
if (!NT_SUCCESS(status)) {
|
|
ExFreePool(uniqueId);
|
|
KeReleaseSemaphore(&Extension->Mutex, IO_NO_INCREMENT, 1, FALSE);
|
|
ExFreePool(entry);
|
|
ZwClose(indexHandle);
|
|
CloseRemoteDatabase(remoteDatabaseHandle);
|
|
ReleaseRemoteDatabaseSemaphore(Extension);
|
|
return;
|
|
}
|
|
} else {
|
|
status = DeleteRemoteDatabaseEntry(remoteDatabaseHandle, offset);
|
|
if (!NT_SUCCESS(status)) {
|
|
ExFreePool(uniqueId);
|
|
KeReleaseSemaphore(&Extension->Mutex, IO_NO_INCREMENT, 1, FALSE);
|
|
ExFreePool(entry);
|
|
ZwClose(indexHandle);
|
|
CloseRemoteDatabase(remoteDatabaseHandle);
|
|
ReleaseRemoteDatabaseSemaphore(Extension);
|
|
return;
|
|
}
|
|
|
|
ExFreePool(entry);
|
|
|
|
entryLength = sizeof(MOUNTMGR_FILE_ENTRY) + volumeName.Length + uniqueId->UniqueIdLength;
|
|
entry = ExAllocatePool(PagedPool, entryLength);
|
|
if (!entry) {
|
|
ExFreePool(uniqueId);
|
|
KeReleaseSemaphore(&Extension->Mutex, IO_NO_INCREMENT, 1, FALSE);
|
|
ZwClose(indexHandle);
|
|
CloseRemoteDatabase(remoteDatabaseHandle);
|
|
ReleaseRemoteDatabaseSemaphore(Extension);
|
|
return;
|
|
}
|
|
|
|
entry->EntryLength = entryLength;
|
|
entry->RefCount = 1;
|
|
entry->VolumeNameOffset = sizeof(MOUNTMGR_FILE_ENTRY);
|
|
entry->VolumeNameLength = volumeName.Length;
|
|
entry->UniqueIdOffset = entry->VolumeNameOffset + entry->VolumeNameLength;
|
|
entry->UniqueIdLength = uniqueId->UniqueIdLength;
|
|
|
|
RtlCopyMemory((PCHAR) entry + entry->VolumeNameOffset, volumeName.Buffer, entry->VolumeNameLength);
|
|
RtlCopyMemory((PCHAR) entry + entry->UniqueIdOffset, uniqueId->UniqueId, entry->UniqueIdLength);
|
|
|
|
status = AddRemoteDatabaseEntry(remoteDatabaseHandle, entry);
|
|
if (!NT_SUCCESS(status)) {
|
|
ExFreePool(entry);
|
|
ExFreePool(uniqueId);
|
|
KeReleaseSemaphore(&Extension->Mutex, IO_NO_INCREMENT, 1, FALSE);
|
|
ZwClose(indexHandle);
|
|
CloseRemoteDatabase(remoteDatabaseHandle);
|
|
ReleaseRemoteDatabaseSemaphore(Extension);
|
|
return;
|
|
}
|
|
}
|
|
|
|
ExFreePool(uniqueId);
|
|
} else {
|
|
status = WriteUniqueIdToMaster(Extension, entry);
|
|
if (!NT_SUCCESS(status)) {
|
|
KeReleaseSemaphore(&Extension->Mutex, IO_NO_INCREMENT, 1, FALSE);
|
|
ExFreePool(entry);
|
|
ZwClose(indexHandle);
|
|
CloseRemoteDatabase(remoteDatabaseHandle);
|
|
ReleaseRemoteDatabaseSemaphore(Extension);
|
|
return;
|
|
}
|
|
|
|
entry->RefCount++;
|
|
status = WriteRemoteDatabaseEntry(remoteDatabaseHandle, offset, entry);
|
|
if (!NT_SUCCESS(status)) {
|
|
KeReleaseSemaphore(&Extension->Mutex, IO_NO_INCREMENT, 1, FALSE);
|
|
ExFreePool(entry);
|
|
ZwClose(indexHandle);
|
|
CloseRemoteDatabase(remoteDatabaseHandle);
|
|
ReleaseRemoteDatabaseSemaphore(Extension);
|
|
return;
|
|
}
|
|
}
|
|
|
|
KeReleaseSemaphore(&Extension->Mutex, IO_NO_INCREMENT, 1, FALSE);
|
|
}
|
|
|
|
ExFreePool(entry);
|
|
}
|
|
|
|
ZwClose(indexHandle);
|
|
|
|
KeWaitForSingleObject(&Extension->Mutex, Executive, KernelMode, FALSE, NULL);
|
|
|
|
for (l = Extension->MountedDeviceList.Flink; l != &Extension->MountedDeviceList; l = l->Flink) {
|
|
deviceInfo = CONTAINING_RECORD(l, MOUNTED_DEVICE_INFORMATION, ListEntry);
|
|
if (deviceInfo == DeviceInfo) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (l == &Extension->MountedDeviceList) {
|
|
deviceInfo = NULL;
|
|
}
|
|
|
|
offset = 0;
|
|
for (;;) {
|
|
entry = GetRemoteDatabaseEntry(remoteDatabaseHandle, offset);
|
|
if (!entry) {
|
|
break;
|
|
}
|
|
|
|
if (!entry->RefCount) {
|
|
status = DeleteRemoteDatabaseEntry(remoteDatabaseHandle, offset);
|
|
if (!NT_SUCCESS(status)) {
|
|
ExFreePool(entry);
|
|
KeReleaseSemaphore(&Extension->Mutex, IO_NO_INCREMENT, 1, FALSE);
|
|
CloseRemoteDatabase(remoteDatabaseHandle);
|
|
ReleaseRemoteDatabaseSemaphore(Extension);
|
|
return;
|
|
}
|
|
|
|
ExFreePool(entry);
|
|
continue;
|
|
}
|
|
|
|
if (deviceInfo) {
|
|
UpdateReplicatedUniqueIds(deviceInfo, entry);
|
|
}
|
|
|
|
offset += entry->EntryLength;
|
|
ExFreePool(entry);
|
|
}
|
|
|
|
KeReleaseSemaphore(&Extension->Mutex, IO_NO_INCREMENT, 1, FALSE);
|
|
|
|
CloseRemoteDatabase(remoteDatabaseHandle);
|
|
ReleaseRemoteDatabaseSemaphore(Extension);
|
|
}
|
|
|
|
|
|
VOID
|
|
ReconcileThisDatabaseWithMaster(
|
|
IN PDEVICE_EXTENSION Extension,
|
|
IN PMOUNTED_DEVICE_INFORMATION DeviceInfo
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
This routine reconciles the remote database with the master database.
|
|
Arguments:
|
|
DeviceInfo - Supplies the device information.
|
|
--*/
|
|
{
|
|
PRECONCILE_WORK_ITEM workItem;
|
|
|
|
if (DeviceInfo->IsRemovable) {
|
|
return;
|
|
}
|
|
|
|
workItem = ExAllocatePool(NonPagedPoolMustSucceed, sizeof(RECONCILE_WORK_ITEM));
|
|
if (!workItem) {
|
|
return;
|
|
}
|
|
|
|
ExInitializeWorkItem(&workItem->WorkItem, ReconcileThisDatabaseWithMasterWorker, &workItem->WorkItemInfo);
|
|
workItem->WorkItemInfo.Extension = Extension;
|
|
workItem->WorkItemInfo.DeviceInfo = DeviceInfo;
|
|
|
|
QueueWorkItem(Extension, &workItem->WorkItem);
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
DeleteFromLocalDatabaseRoutine(
|
|
IN PWSTR ValueName,
|
|
IN ULONG ValueType,
|
|
IN PVOID ValueData,
|
|
IN ULONG ValueLength,
|
|
IN PVOID Context,
|
|
IN PVOID EntryContext
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
This routine queries the unique id for the given value.
|
|
Arguments:
|
|
ValueName - Supplies the name of the registry value.
|
|
ValueType - Supplies the type of the registry value.
|
|
ValueData - Supplies the data of the registry value.
|
|
ValueLength - Supplies the length of the registry value.
|
|
Context - Supplies the unique id.
|
|
EntryContext - Not used.
|
|
Return Value:
|
|
NTSTATUS
|
|
--*/
|
|
{
|
|
PMOUNTDEV_UNIQUE_ID uniqueId = Context;
|
|
|
|
if (uniqueId->UniqueIdLength == ValueLength && RtlCompareMemory(uniqueId->UniqueId, ValueData, ValueLength) == ValueLength) {
|
|
RtlDeleteRegistryValue(RTL_REGISTRY_ABSOLUTE, MOUNTED_DEVICES_KEY, ValueName);
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
VOID
|
|
DeleteFromLocalDatabase(
|
|
IN PUNICODE_STRING SymbolicLinkName,
|
|
IN PMOUNTDEV_UNIQUE_ID UniqueId
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
This routine makes sure that the given symbolic link names exists in the local database and that its unique id is equal to the one given.
|
|
If these two conditions are true then this local database entry is deleted.
|
|
Arguments:
|
|
SymbolicLinkName - Supplies the symbolic link name.
|
|
UniqueId - Supplies the unique id.
|
|
--*/
|
|
{
|
|
RTL_QUERY_REGISTRY_TABLE queryTable[2];
|
|
|
|
RtlZeroMemory(queryTable, 2*sizeof(RTL_QUERY_REGISTRY_TABLE));
|
|
queryTable[0].QueryRoutine = DeleteFromLocalDatabaseRoutine;
|
|
queryTable[0].Flags = RTL_QUERY_REGISTRY_REQUIRED;
|
|
queryTable[0].Name = SymbolicLinkName->Buffer;
|
|
|
|
RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE, MOUNTED_DEVICES_KEY, queryTable, UniqueId, NULL);
|
|
}
|
|
|
|
|
|
PSAVED_LINKS_INFORMATION
|
|
RemoveSavedLinks(
|
|
IN PDEVICE_EXTENSION Extension,
|
|
IN PMOUNTDEV_UNIQUE_ID UniqueId
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
This routine finds and removed the given unique id from the saved links list.
|
|
Arguments:
|
|
Extension - Supplies the device extension.
|
|
UniqueId - Supplies the unique id.
|
|
Return Value:
|
|
The removed saved links list or NULL.
|
|
--*/
|
|
{
|
|
PLIST_ENTRY l;
|
|
PSAVED_LINKS_INFORMATION savedLinks;
|
|
|
|
for (l = Extension->SavedLinksList.Flink; l != &Extension->SavedLinksList; l = l->Flink) {
|
|
savedLinks = CONTAINING_RECORD(l, SAVED_LINKS_INFORMATION, ListEntry);
|
|
if (savedLinks->UniqueId->UniqueIdLength != UniqueId->UniqueIdLength) {
|
|
continue;
|
|
}
|
|
|
|
if (RtlCompareMemory(savedLinks->UniqueId->UniqueId, UniqueId->UniqueId, UniqueId->UniqueIdLength) == UniqueId->UniqueIdLength) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (l == &Extension->SavedLinksList) {
|
|
return NULL;
|
|
}
|
|
|
|
RemoveEntryList(l);
|
|
|
|
return savedLinks;
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
RedirectSavedLink(
|
|
IN PSAVED_LINKS_INFORMATION SavedLinks,
|
|
IN PUNICODE_STRING SymbolicLinkName,
|
|
IN PUNICODE_STRING DeviceName
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
This routine attempts to redirect the given link to the given device name if this link is in the saved links list.
|
|
When this is done, the symbolic link entry is removed from the saved links list.
|
|
Return Value:
|
|
FALSE - The link was not successfully redirected.
|
|
TRUE - The link was successfully redirected.
|
|
--*/
|
|
{
|
|
PLIST_ENTRY l;
|
|
PSYMBOLIC_LINK_NAME_ENTRY symlinkEntry;
|
|
|
|
for (l = SavedLinks->SymbolicLinkNames.Flink; l != &SavedLinks->SymbolicLinkNames; l = l->Flink) {
|
|
symlinkEntry = CONTAINING_RECORD(l, SYMBOLIC_LINK_NAME_ENTRY, ListEntry);
|
|
if (RtlEqualUnicodeString(SymbolicLinkName, &symlinkEntry->SymbolicLinkName, TRUE)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (l == &SavedLinks->SymbolicLinkNames) {
|
|
return FALSE;
|
|
}
|
|
|
|
// NOTE There is a small window here where the drive letter could be
|
|
// taken away. This is the best we can do without more support from OB.
|
|
|
|
IoDeleteSymbolicLink(SymbolicLinkName);
|
|
IoCreateSymbolicLink(SymbolicLinkName, DeviceName);
|
|
|
|
ExFreePool(symlinkEntry->SymbolicLinkName.Buffer);
|
|
ExFreePool(symlinkEntry);
|
|
RemoveEntryList(l);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOLEAN IsOffline(IN PUNICODE_STRING SymbolicLinkName)
|
|
/*++
|
|
Routine Description:
|
|
This routine checks to see if the given name has been marked to be an offline volume.
|
|
Arguments:
|
|
SymbolicLinkName - Supplies the symbolic link name.
|
|
Return Value:
|
|
FALSE - This volume is not marked for offline.
|
|
TRUE - This volume is marked for offline.
|
|
--*/
|
|
{
|
|
ULONG zero, offline;
|
|
RTL_QUERY_REGISTRY_TABLE queryTable[2];
|
|
NTSTATUS status;
|
|
|
|
zero = 0;
|
|
|
|
RtlZeroMemory(queryTable, 2*sizeof(RTL_QUERY_REGISTRY_TABLE));
|
|
queryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
|
queryTable[0].Name = SymbolicLinkName->Buffer;
|
|
queryTable[0].EntryContext = &offline;
|
|
queryTable[0].DefaultType = REG_DWORD;
|
|
queryTable[0].DefaultData = &zero;
|
|
queryTable[0].DefaultLength = sizeof(ULONG);
|
|
|
|
status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE, MOUNTED_DEVICES_OFFLINE_KEY, queryTable, NULL, NULL);
|
|
if (!NT_SUCCESS(status)) {
|
|
offline = 0;
|
|
}
|
|
|
|
return offline ? TRUE : FALSE;
|
|
}
|
|
|
|
|
|
VOID SendOnlineNotification(IN PUNICODE_STRING NotificationName)
|
|
/*++
|
|
Routine Description:
|
|
This routine sends an ONLINE notification to the given device.
|
|
Arguments:
|
|
NotificationName - Supplies the notification name.
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
PFILE_OBJECT fileObject;
|
|
PDEVICE_OBJECT deviceObject;
|
|
KEVENT event;
|
|
PIRP irp;
|
|
IO_STATUS_BLOCK ioStatus;
|
|
PIO_STACK_LOCATION irpSp;
|
|
|
|
status = IoGetDeviceObjectPointer(NotificationName, FILE_READ_ATTRIBUTES, &fileObject, &deviceObject);
|
|
if (!NT_SUCCESS(status)) {
|
|
return;
|
|
}
|
|
deviceObject = IoGetAttachedDeviceReference(fileObject->DeviceObject);
|
|
|
|
KeInitializeEvent(&event, NotificationEvent, FALSE);
|
|
irp = IoBuildDeviceIoControlRequest(IOCTL_VOLUME_ONLINE, deviceObject, NULL, 0, NULL, 0, FALSE, &event, &ioStatus);
|
|
if (!irp) {
|
|
ObDereferenceObject(deviceObject);
|
|
ObDereferenceObject(fileObject);
|
|
return;
|
|
}
|
|
irpSp = IoGetNextIrpStackLocation(irp);
|
|
irpSp->FileObject = fileObject;
|
|
|
|
status = IoCallDriver(deviceObject, irp);
|
|
if (status == STATUS_PENDING) {
|
|
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
|
|
status = ioStatus.Status;
|
|
}
|
|
|
|
ObDereferenceObject(deviceObject);
|
|
ObDereferenceObject(fileObject);
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
MountMgrTargetDeviceNotification(
|
|
IN PVOID NotificationStructure,
|
|
IN PVOID DeviceInfo
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
This routine processes target device notifications.
|
|
Arguments:
|
|
NotificationStructure - Supplies the notification structure.
|
|
DeviceInfo - Supplies the device information.
|
|
--*/
|
|
{
|
|
PTARGET_DEVICE_REMOVAL_NOTIFICATION notification = NotificationStructure;
|
|
PMOUNTED_DEVICE_INFORMATION deviceInfo = DeviceInfo;
|
|
PDEVICE_EXTENSION extension = deviceInfo->Extension;
|
|
|
|
if (!IsEqualGUID(¬ification->Event, &GUID_TARGET_DEVICE_REMOVE_COMPLETE)) {
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
MountMgrMountedDeviceRemoval(extension, &deviceInfo->NotificationName);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
VOID
|
|
RegisterForTargetDeviceNotification(
|
|
IN PDEVICE_EXTENSION Extension,
|
|
IN PMOUNTED_DEVICE_INFORMATION DeviceInfo
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
This routine registers for target device notification so that the symbolic link to a device interface can be removed in a timely manner.
|
|
Arguments:
|
|
Extension - Supplies the device extension.
|
|
DeviceInfo - Supplies the device information.
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
PFILE_OBJECT fileObject;
|
|
PDEVICE_OBJECT deviceObject;
|
|
|
|
status = IoGetDeviceObjectPointer(&DeviceInfo->DeviceName, FILE_READ_ATTRIBUTES, &fileObject, &deviceObject);
|
|
if (!NT_SUCCESS(status)) {
|
|
return;
|
|
}
|
|
|
|
status = IoRegisterPlugPlayNotification(EventCategoryTargetDeviceChange, 0, fileObject, Extension->DriverObject, MountMgrTargetDeviceNotification, DeviceInfo, &DeviceInfo->TargetDeviceNotificationEntry);
|
|
if (!NT_SUCCESS(status)) {
|
|
DeviceInfo->TargetDeviceNotificationEntry = NULL;
|
|
}
|
|
|
|
ObDereferenceObject(fileObject);
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
MountMgrMountedDeviceArrival(
|
|
IN PDEVICE_EXTENSION Extension,
|
|
IN PUNICODE_STRING NotificationName,
|
|
IN BOOLEAN NotAPdo
|
|
)
|
|
{
|
|
PDEVICE_EXTENSION extension = Extension;
|
|
PMOUNTED_DEVICE_INFORMATION deviceInfo, d;
|
|
NTSTATUS status;
|
|
UNICODE_STRING targetName, otherTargetName;
|
|
PMOUNTDEV_UNIQUE_ID uniqueId;
|
|
BOOLEAN isRecognized;
|
|
UNICODE_STRING suggestedName;
|
|
BOOLEAN useOnlyIfThereAreNoOtherLinks;
|
|
PUNICODE_STRING symbolicLinkNames;
|
|
ULONG numNames, i;
|
|
BOOLEAN hasDriveLetter, offline;
|
|
BOOLEAN hasVolumeName, isLinkPreset;
|
|
PSYMBOLIC_LINK_NAME_ENTRY symlinkEntry;
|
|
UNICODE_STRING volumeName;
|
|
UNICODE_STRING driveLetterName;
|
|
PSAVED_LINKS_INFORMATION savedLinks;
|
|
PLIST_ENTRY l;
|
|
|
|
deviceInfo = ExAllocatePool(PagedPool, sizeof(MOUNTED_DEVICE_INFORMATION));
|
|
if (!deviceInfo) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
RtlZeroMemory(deviceInfo, sizeof(MOUNTED_DEVICE_INFORMATION));
|
|
|
|
InitializeListHead(&deviceInfo->SymbolicLinkNames);
|
|
InitializeListHead(&deviceInfo->ReplicatedUniqueIds);
|
|
|
|
deviceInfo->NotificationName.Length = NotificationName->Length;
|
|
deviceInfo->NotificationName.MaximumLength = deviceInfo->NotificationName.Length + sizeof(WCHAR);
|
|
deviceInfo->NotificationName.Buffer = ExAllocatePool(PagedPool, deviceInfo->NotificationName.MaximumLength);
|
|
if (!deviceInfo->NotificationName.Buffer) {
|
|
ExFreePool(deviceInfo);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
RtlCopyMemory(deviceInfo->NotificationName.Buffer, NotificationName->Buffer, deviceInfo->NotificationName.Length);
|
|
deviceInfo->NotificationName.Buffer[deviceInfo->NotificationName.Length/sizeof(WCHAR)] = 0;
|
|
deviceInfo->NotAPdo = NotAPdo;
|
|
deviceInfo->Extension = extension;
|
|
|
|
status = QueryDeviceInformation(NotificationName, &targetName, &uniqueId, &deviceInfo->IsRemovable, &isRecognized);
|
|
if (!NT_SUCCESS(status)) {
|
|
KeWaitForSingleObject(&extension->Mutex, Executive, KernelMode, FALSE, NULL);
|
|
for (l = extension->DeadMountedDeviceList.Flink; l != &extension->DeadMountedDeviceList; l = l->Flink) {
|
|
d = CONTAINING_RECORD(l, MOUNTED_DEVICE_INFORMATION, ListEntry);
|
|
if (RtlEqualUnicodeString(&deviceInfo->NotificationName, &d->NotificationName, TRUE)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (l == &extension->DeadMountedDeviceList) {
|
|
InsertTailList(&extension->DeadMountedDeviceList, &deviceInfo->ListEntry);
|
|
} else {
|
|
ExFreePool(deviceInfo->NotificationName.Buffer);
|
|
ExFreePool(deviceInfo);
|
|
}
|
|
|
|
KeReleaseSemaphore(&extension->Mutex, IO_NO_INCREMENT, 1, FALSE);
|
|
|
|
return status;
|
|
}
|
|
|
|
deviceInfo->UniqueId = uniqueId;
|
|
deviceInfo->DeviceName = targetName;
|
|
deviceInfo->KeepLinksWhenOffline = FALSE;
|
|
|
|
status = QuerySuggestedLinkName(&deviceInfo->NotificationName, &suggestedName, &useOnlyIfThereAreNoOtherLinks);
|
|
if (!NT_SUCCESS(status)) {
|
|
suggestedName.Buffer = NULL;
|
|
}
|
|
|
|
if (suggestedName.Buffer && IsDriveLetter(&suggestedName)) {
|
|
deviceInfo->SuggestedDriveLetter = (UCHAR) suggestedName.Buffer[12];
|
|
} else {
|
|
deviceInfo->SuggestedDriveLetter = 0;
|
|
}
|
|
|
|
KeWaitForSingleObject(&extension->Mutex, Executive, KernelMode, FALSE, NULL);
|
|
|
|
for (l = extension->MountedDeviceList.Flink; l != &extension->MountedDeviceList; l = l->Flink) {
|
|
d = CONTAINING_RECORD(l, MOUNTED_DEVICE_INFORMATION, ListEntry);
|
|
if (!RtlCompareUnicodeString(&d->DeviceName, &targetName, TRUE)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (l != &extension->MountedDeviceList) {
|
|
if (suggestedName.Buffer) {
|
|
ExFreePool(suggestedName.Buffer);
|
|
}
|
|
ExFreePool(uniqueId);
|
|
ExFreePool(targetName.Buffer);
|
|
ExFreePool(deviceInfo->NotificationName.Buffer);
|
|
ExFreePool(deviceInfo);
|
|
KeReleaseSemaphore(&extension->Mutex, IO_NO_INCREMENT, 1, FALSE);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
status = QuerySymbolicLinkNamesFromStorage(extension, deviceInfo, suggestedName.Buffer ? &suggestedName : NULL, useOnlyIfThereAreNoOtherLinks, &symbolicLinkNames, &numNames);
|
|
|
|
if (suggestedName.Buffer) {
|
|
ExFreePool(suggestedName.Buffer);
|
|
}
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
symbolicLinkNames = NULL;
|
|
numNames = 0;
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
|
|
savedLinks = RemoveSavedLinks(extension, uniqueId);
|
|
|
|
hasDriveLetter = FALSE;
|
|
offline = FALSE;
|
|
hasVolumeName = FALSE;
|
|
for (i = 0; i < numNames; i++) {
|
|
if (MOUNTMGR_IS_VOLUME_NAME(&symbolicLinkNames[i])) {
|
|
hasVolumeName = TRUE;
|
|
} else if (IsDriveLetter(&symbolicLinkNames[i])) {
|
|
if (hasDriveLetter) {
|
|
DeleteFromLocalDatabase(&symbolicLinkNames[i], uniqueId);
|
|
continue;
|
|
}
|
|
hasDriveLetter = TRUE;
|
|
}
|
|
|
|
status = IoCreateSymbolicLink(&symbolicLinkNames[i], &targetName);
|
|
if (!NT_SUCCESS(status)) {
|
|
isLinkPreset = TRUE;
|
|
if (!savedLinks || !RedirectSavedLink(savedLinks, &symbolicLinkNames[i], &targetName)) {
|
|
status = QueryDeviceInformation(&symbolicLinkNames[i], &otherTargetName, NULL, NULL, NULL);
|
|
if (!NT_SUCCESS(status)) {
|
|
isLinkPreset = FALSE;
|
|
}
|
|
|
|
if (isLinkPreset && !RtlEqualUnicodeString(&targetName, &otherTargetName, TRUE)) {
|
|
isLinkPreset = FALSE;
|
|
}
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
ExFreePool(otherTargetName.Buffer);
|
|
}
|
|
}
|
|
|
|
if (!isLinkPreset) {
|
|
if (IsDriveLetter(&symbolicLinkNames[i])) {
|
|
hasDriveLetter = FALSE;
|
|
DeleteFromLocalDatabase(&symbolicLinkNames[i], uniqueId);
|
|
}
|
|
|
|
ExFreePool(symbolicLinkNames[i].Buffer);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (IsOffline(&symbolicLinkNames[i])) {
|
|
offline = TRUE;
|
|
}
|
|
|
|
symlinkEntry = ExAllocatePool(PagedPool, sizeof(SYMBOLIC_LINK_NAME_ENTRY));
|
|
if (!symlinkEntry) {
|
|
IoDeleteSymbolicLink(&symbolicLinkNames[i]);
|
|
ExFreePool(symbolicLinkNames[i].Buffer);
|
|
continue;
|
|
}
|
|
|
|
symlinkEntry->SymbolicLinkName = symbolicLinkNames[i];
|
|
symlinkEntry->IsInDatabase = TRUE;
|
|
|
|
InsertTailList(&deviceInfo->SymbolicLinkNames, &symlinkEntry->ListEntry);
|
|
}
|
|
|
|
for (l = deviceInfo->SymbolicLinkNames.Flink; l != &deviceInfo->SymbolicLinkNames; l = l->Flink) {
|
|
symlinkEntry = CONTAINING_RECORD(l, SYMBOLIC_LINK_NAME_ENTRY, ListEntry);
|
|
SendLinkCreated(&symlinkEntry->SymbolicLinkName);
|
|
}
|
|
|
|
if (savedLinks) {
|
|
while (!IsListEmpty(&savedLinks->SymbolicLinkNames)) {
|
|
l = RemoveHeadList(&deviceInfo->SymbolicLinkNames);
|
|
symlinkEntry = CONTAINING_RECORD(l, SYMBOLIC_LINK_NAME_ENTRY, ListEntry);
|
|
IoDeleteSymbolicLink(&symlinkEntry->SymbolicLinkName);
|
|
ExFreePool(symlinkEntry->SymbolicLinkName.Buffer);
|
|
ExFreePool(symlinkEntry);
|
|
}
|
|
ExFreePool(savedLinks->UniqueId);
|
|
ExFreePool(savedLinks);
|
|
}
|
|
|
|
if (!hasVolumeName) {
|
|
status = CreateNewVolumeName(&volumeName);
|
|
if (NT_SUCCESS(status)) {
|
|
RtlWriteRegistryValue(RTL_REGISTRY_ABSOLUTE, MOUNTED_DEVICES_KEY, volumeName.Buffer, REG_BINARY, uniqueId->UniqueId, uniqueId->UniqueIdLength);
|
|
IoCreateSymbolicLink(&volumeName, &targetName);
|
|
symlinkEntry = ExAllocatePool(PagedPool, sizeof(SYMBOLIC_LINK_NAME_ENTRY));
|
|
if (symlinkEntry) {
|
|
symlinkEntry->SymbolicLinkName = volumeName;
|
|
symlinkEntry->IsInDatabase = TRUE;
|
|
InsertTailList(&deviceInfo->SymbolicLinkNames, &symlinkEntry->ListEntry);
|
|
SendLinkCreated(&volumeName);
|
|
} else {
|
|
ExFreePool(volumeName.Buffer);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (hasDriveLetter) {
|
|
deviceInfo->SuggestedDriveLetter = 0;
|
|
}
|
|
|
|
if (!hasDriveLetter && extension->AutomaticDriveLetterAssignment && (isRecognized || deviceInfo->SuggestedDriveLetter) && !HasNoDriveLetterEntry(uniqueId)) {
|
|
status = CreateNewDriveLetterName(&driveLetterName, &targetName, deviceInfo->SuggestedDriveLetter, uniqueId);
|
|
if (NT_SUCCESS(status)) {
|
|
RtlWriteRegistryValue(RTL_REGISTRY_ABSOLUTE, MOUNTED_DEVICES_KEY, driveLetterName.Buffer, REG_BINARY, uniqueId->UniqueId, uniqueId->UniqueIdLength);
|
|
symlinkEntry = ExAllocatePool(PagedPool, sizeof(SYMBOLIC_LINK_NAME_ENTRY));
|
|
if (symlinkEntry) {
|
|
symlinkEntry->SymbolicLinkName = driveLetterName;
|
|
symlinkEntry->IsInDatabase = TRUE;
|
|
InsertTailList(&deviceInfo->SymbolicLinkNames, &symlinkEntry->ListEntry);
|
|
SendLinkCreated(&driveLetterName);
|
|
} else {
|
|
ExFreePool(driveLetterName.Buffer);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!NotAPdo) {
|
|
RegisterForTargetDeviceNotification(extension, deviceInfo);
|
|
}
|
|
|
|
InsertTailList(&extension->MountedDeviceList, &deviceInfo->ListEntry);
|
|
KeReleaseSemaphore(&extension->Mutex, IO_NO_INCREMENT, 1, FALSE);
|
|
|
|
if (!offline) {
|
|
SendOnlineNotification(NotificationName);
|
|
}
|
|
|
|
if (symbolicLinkNames) {
|
|
ExFreePool(symbolicLinkNames);
|
|
}
|
|
|
|
IssueUniqueIdChangeNotify(extension, NotificationName, uniqueId);
|
|
if (extension->AutomaticDriveLetterAssignment) {
|
|
KeWaitForSingleObject(&extension->Mutex, Executive, KernelMode, FALSE, NULL);
|
|
ReconcileThisDatabaseWithMaster(extension, deviceInfo);
|
|
KeReleaseSemaphore(&extension->Mutex, IO_NO_INCREMENT, 1, FALSE);
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
MountMgrMountedDeviceRemoval(
|
|
IN PDEVICE_EXTENSION Extension,
|
|
IN PUNICODE_STRING NotificationName
|
|
)
|
|
{
|
|
PDEVICE_EXTENSION extension = Extension;
|
|
PMOUNTED_DEVICE_INFORMATION deviceInfo;
|
|
PSYMBOLIC_LINK_NAME_ENTRY symlinkEntry;
|
|
PLIST_ENTRY l, ll;
|
|
PREPLICATED_UNIQUE_ID replUniqueId;
|
|
PSAVED_LINKS_INFORMATION savedLinks;
|
|
|
|
KeWaitForSingleObject(&extension->Mutex, Executive, KernelMode, FALSE, NULL);
|
|
for (l = extension->MountedDeviceList.Flink; l != &extension->MountedDeviceList; l = l->Flink) {
|
|
deviceInfo = CONTAINING_RECORD(l, MOUNTED_DEVICE_INFORMATION, ListEntry);
|
|
if (!RtlCompareUnicodeString(&deviceInfo->NotificationName, NotificationName, TRUE)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (l != &extension->MountedDeviceList) {
|
|
if (deviceInfo->KeepLinksWhenOffline) {
|
|
savedLinks = ExAllocatePool(PagedPool, sizeof(SAVED_LINKS_INFORMATION));
|
|
if (!savedLinks) {
|
|
deviceInfo->KeepLinksWhenOffline = FALSE;
|
|
}
|
|
}
|
|
|
|
if (deviceInfo->KeepLinksWhenOffline) {
|
|
InsertTailList(&extension->SavedLinksList, &savedLinks->ListEntry);
|
|
InitializeListHead(&savedLinks->SymbolicLinkNames);
|
|
savedLinks->UniqueId = deviceInfo->UniqueId;
|
|
while (!IsListEmpty(&deviceInfo->SymbolicLinkNames)) {
|
|
ll = RemoveHeadList(&deviceInfo->SymbolicLinkNames);
|
|
symlinkEntry = CONTAINING_RECORD(ll, SYMBOLIC_LINK_NAME_ENTRY, ListEntry);
|
|
if (symlinkEntry->IsInDatabase) {
|
|
InsertTailList(&savedLinks->SymbolicLinkNames, ll);
|
|
} else {
|
|
IoDeleteSymbolicLink(&symlinkEntry->SymbolicLinkName);
|
|
ExFreePool(symlinkEntry->SymbolicLinkName.Buffer);
|
|
ExFreePool(symlinkEntry);
|
|
}
|
|
}
|
|
} else {
|
|
while (!IsListEmpty(&deviceInfo->SymbolicLinkNames)) {
|
|
ll = RemoveHeadList(&deviceInfo->SymbolicLinkNames);
|
|
symlinkEntry = CONTAINING_RECORD(ll, SYMBOLIC_LINK_NAME_ENTRY, ListEntry);
|
|
IoDeleteSymbolicLink(&symlinkEntry->SymbolicLinkName);
|
|
ExFreePool(symlinkEntry->SymbolicLinkName.Buffer);
|
|
ExFreePool(symlinkEntry);
|
|
}
|
|
}
|
|
|
|
while (!IsListEmpty(&deviceInfo->ReplicatedUniqueIds)) {
|
|
ll = RemoveHeadList(&deviceInfo->ReplicatedUniqueIds);
|
|
replUniqueId = CONTAINING_RECORD(ll, REPLICATED_UNIQUE_ID, ListEntry);
|
|
ExFreePool(replUniqueId->UniqueId);
|
|
ExFreePool(replUniqueId);
|
|
}
|
|
|
|
RemoveEntryList(l);
|
|
ExFreePool(deviceInfo->NotificationName.Buffer);
|
|
|
|
if (!deviceInfo->KeepLinksWhenOffline) {
|
|
ExFreePool(deviceInfo->UniqueId);
|
|
}
|
|
|
|
ExFreePool(deviceInfo->DeviceName.Buffer);
|
|
|
|
if (deviceInfo->TargetDeviceNotificationEntry) {
|
|
IoUnregisterPlugPlayNotification(deviceInfo->TargetDeviceNotificationEntry);
|
|
}
|
|
|
|
ExFreePool(deviceInfo);
|
|
} else {
|
|
for (l = extension->DeadMountedDeviceList.Flink; l != &extension->DeadMountedDeviceList; l = l->Flink) {
|
|
deviceInfo = CONTAINING_RECORD(l, MOUNTED_DEVICE_INFORMATION, ListEntry);
|
|
if (!RtlCompareUnicodeString(&deviceInfo->NotificationName, NotificationName, TRUE)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (l != &extension->DeadMountedDeviceList) {
|
|
RemoveEntryList(l);
|
|
ExFreePool(deviceInfo->NotificationName.Buffer);
|
|
ExFreePool(deviceInfo);
|
|
}
|
|
}
|
|
|
|
KeReleaseSemaphore(&extension->Mutex, IO_NO_INCREMENT, 1, FALSE);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
MountMgrMountedDeviceNotification(
|
|
IN PVOID NotificationStructure,
|
|
IN PVOID Extension
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
This routine is called whenever a volume comes or goes.
|
|
Arguments:
|
|
NotificationStructure - Supplies the notification structure.
|
|
Extension - Supplies the device extension.
|
|
--*/
|
|
{
|
|
PDEVICE_INTERFACE_CHANGE_NOTIFICATION notification = NotificationStructure;
|
|
PDEVICE_EXTENSION extension = Extension;
|
|
BOOLEAN oldHardErrorMode;
|
|
NTSTATUS status;
|
|
|
|
oldHardErrorMode = PsGetCurrentThread()->HardErrorsAreDisabled;
|
|
PsGetCurrentThread()->HardErrorsAreDisabled = TRUE;
|
|
|
|
if (IsEqualGUID(¬ification->Event, &GUID_DEVICE_INTERFACE_ARRIVAL)) {
|
|
status = MountMgrMountedDeviceArrival(extension, notification->SymbolicLinkName, FALSE);
|
|
} else if (IsEqualGUID(¬ification->Event, &GUID_DEVICE_INTERFACE_REMOVAL)) {
|
|
status = MountMgrMountedDeviceRemoval(extension, notification->SymbolicLinkName);
|
|
} else {
|
|
status = STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
PsGetCurrentThread()->HardErrorsAreDisabled = oldHardErrorMode;
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
MountMgrCreateClose(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
This routine is the dispatch for a create or close requests.
|
|
Arguments:
|
|
DeviceObject - Supplies the device object.
|
|
Irp - Supplies the I/O request packet.
|
|
Return Value:
|
|
NTSTATUS
|
|
--*/
|
|
{
|
|
PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
NTSTATUS status;
|
|
|
|
if (irpSp->MajorFunction == IRP_MJ_CREATE) {
|
|
if (irpSp->Parameters.Create.Options&FILE_DIRECTORY_FILE) {
|
|
status = STATUS_NOT_A_DIRECTORY;
|
|
} else {
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
} else {
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
|
|
Irp->IoStatus.Status = status;
|
|
Irp->IoStatus.Information = 0;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
MountMgrCreatePoint(
|
|
IN OUT PDEVICE_EXTENSION Extension,
|
|
IN OUT PIRP Irp
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
This routine creates a mount point.
|
|
Arguments:
|
|
Extension - Supplies the device extension.
|
|
Irp - Supplies the I/O request packet.
|
|
Return Value:
|
|
NTSTATUS
|
|
--*/
|
|
{
|
|
PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
PMOUNTMGR_CREATE_POINT_INPUT input = Irp->AssociatedIrp.SystemBuffer;
|
|
ULONG len1, len2, len;
|
|
UNICODE_STRING symbolicLinkName, deviceName;
|
|
|
|
if (irpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(MOUNTMGR_CREATE_POINT_INPUT)) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
len1 = input->DeviceNameOffset + input->DeviceNameLength;
|
|
len2 = input->SymbolicLinkNameOffset + input->SymbolicLinkNameLength;
|
|
len = len1 > len2 ? len1 : len2;
|
|
|
|
if (len > irpSp->Parameters.DeviceIoControl.InputBufferLength) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
symbolicLinkName.Length = symbolicLinkName.MaximumLength = input->SymbolicLinkNameLength;
|
|
symbolicLinkName.Buffer = (PWSTR) ((PCHAR) input + input->SymbolicLinkNameOffset);
|
|
deviceName.Length = deviceName.MaximumLength = input->DeviceNameLength;
|
|
deviceName.Buffer = (PWSTR) ((PCHAR) input + input->DeviceNameOffset);
|
|
|
|
return MountMgrCreatePointWorker(Extension, &symbolicLinkName, &deviceName);
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
QueryPointsFromSymbolicLinkName(
|
|
IN PDEVICE_EXTENSION Extension,
|
|
IN PUNICODE_STRING SymbolicLinkName,
|
|
IN OUT PIRP Irp
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
This routine queries the mount point information from the symbolic link name.
|
|
Arguments:
|
|
SymbolicLinkName - Supplies the symbolic link name.
|
|
Irp - Supplies the I/O request packet.
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
UNICODE_STRING deviceName;
|
|
PLIST_ENTRY l, ll;
|
|
PMOUNTED_DEVICE_INFORMATION deviceInfo;
|
|
PSYMBOLIC_LINK_NAME_ENTRY symEntry;
|
|
ULONG len;
|
|
PIO_STACK_LOCATION irpSp;
|
|
PMOUNTMGR_MOUNT_POINTS output;
|
|
|
|
status = QueryDeviceInformation(SymbolicLinkName, &deviceName, NULL, NULL, NULL);
|
|
if (NT_SUCCESS(status)) {
|
|
for (l = Extension->MountedDeviceList.Flink; l != &Extension->MountedDeviceList; l = l->Flink) {
|
|
deviceInfo = CONTAINING_RECORD(l, MOUNTED_DEVICE_INFORMATION, ListEntry);
|
|
if (!RtlCompareUnicodeString(&deviceName, &deviceInfo->DeviceName, TRUE)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
ExFreePool(deviceName.Buffer);
|
|
|
|
if (l == &Extension->MountedDeviceList) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
for (l = deviceInfo->SymbolicLinkNames.Flink; l != &deviceInfo->SymbolicLinkNames; l = l->Flink) {
|
|
symEntry = CONTAINING_RECORD(l, SYMBOLIC_LINK_NAME_ENTRY, ListEntry);
|
|
if (RtlEqualUnicodeString(SymbolicLinkName, &symEntry->SymbolicLinkName, TRUE)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (l == &deviceInfo->SymbolicLinkNames) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
} else {
|
|
for (l = Extension->MountedDeviceList.Flink; l != &Extension->MountedDeviceList; l = l->Flink) {
|
|
deviceInfo = CONTAINING_RECORD(l, MOUNTED_DEVICE_INFORMATION, ListEntry);
|
|
for (ll = deviceInfo->SymbolicLinkNames.Flink; ll != &deviceInfo->SymbolicLinkNames; ll = ll->Flink) {
|
|
symEntry = CONTAINING_RECORD(ll, SYMBOLIC_LINK_NAME_ENTRY, ListEntry);
|
|
if (RtlEqualUnicodeString(SymbolicLinkName, &symEntry->SymbolicLinkName, TRUE)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (ll != &deviceInfo->SymbolicLinkNames) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (l == &Extension->MountedDeviceList) {
|
|
return STATUS_OBJECT_NAME_NOT_FOUND;
|
|
}
|
|
}
|
|
|
|
len = sizeof(MOUNTMGR_MOUNT_POINTS) + symEntry->SymbolicLinkName.Length + deviceInfo->DeviceName.Length + deviceInfo->UniqueId->UniqueIdLength;
|
|
|
|
irpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
output = Irp->AssociatedIrp.SystemBuffer;
|
|
output->Size = len;
|
|
output->NumberOfMountPoints = 1;
|
|
Irp->IoStatus.Information = len;
|
|
|
|
if (len > irpSp->Parameters.DeviceIoControl.OutputBufferLength) {
|
|
Irp->IoStatus.Information = sizeof(MOUNTMGR_MOUNT_POINTS);
|
|
return STATUS_BUFFER_OVERFLOW;
|
|
}
|
|
|
|
output->MountPoints[0].SymbolicLinkNameOffset = sizeof(MOUNTMGR_MOUNT_POINTS);
|
|
output->MountPoints[0].SymbolicLinkNameLength = symEntry->SymbolicLinkName.Length;
|
|
|
|
if (symEntry->IsInDatabase) {
|
|
output->MountPoints[0].UniqueIdOffset = output->MountPoints[0].SymbolicLinkNameOffset + output->MountPoints[0].SymbolicLinkNameLength;
|
|
output->MountPoints[0].UniqueIdLength = deviceInfo->UniqueId->UniqueIdLength;
|
|
} else {
|
|
output->MountPoints[0].UniqueIdOffset = 0;
|
|
output->MountPoints[0].UniqueIdLength = 0;
|
|
}
|
|
|
|
output->MountPoints[0].DeviceNameOffset = output->MountPoints[0].SymbolicLinkNameOffset + output->MountPoints[0].SymbolicLinkNameLength + output->MountPoints[0].UniqueIdLength;
|
|
output->MountPoints[0].DeviceNameLength = deviceInfo->DeviceName.Length;
|
|
|
|
RtlCopyMemory((PCHAR) output + output->MountPoints[0].SymbolicLinkNameOffset, symEntry->SymbolicLinkName.Buffer, output->MountPoints[0].SymbolicLinkNameLength);
|
|
|
|
if (symEntry->IsInDatabase) {
|
|
RtlCopyMemory((PCHAR) output + output->MountPoints[0].UniqueIdOffset, deviceInfo->UniqueId->UniqueId, output->MountPoints[0].UniqueIdLength);
|
|
}
|
|
|
|
RtlCopyMemory((PCHAR) output + output->MountPoints[0].DeviceNameOffset, deviceInfo->DeviceName.Buffer, output->MountPoints[0].DeviceNameLength);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
QueryPointsFromMemory(
|
|
IN PDEVICE_EXTENSION Extension,
|
|
IN OUT PIRP Irp,
|
|
IN PMOUNTDEV_UNIQUE_ID UniqueId,
|
|
IN PUNICODE_STRING DeviceName
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
This routine queries the points for the given unique id or device name.
|
|
Arguments:
|
|
Extension - Supplies the device extension.
|
|
Irp - Supplies the I/O request packet.
|
|
UniqueId - Supplies the unique id.
|
|
DeviceName - Supplies the device name.
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
UNICODE_STRING targetName;
|
|
ULONG numPoints, size;
|
|
PLIST_ENTRY l, ll;
|
|
PMOUNTED_DEVICE_INFORMATION deviceInfo;
|
|
PSYMBOLIC_LINK_NAME_ENTRY symlinkEntry;
|
|
PIO_STACK_LOCATION irpSp;
|
|
PMOUNTMGR_MOUNT_POINTS output;
|
|
ULONG offset, uOffset, dOffset;
|
|
USHORT uLen, dLen;
|
|
|
|
if (DeviceName) {
|
|
status = QueryDeviceInformation(DeviceName, &targetName, NULL, NULL, NULL);
|
|
if (!NT_SUCCESS(status)) {
|
|
return status;
|
|
}
|
|
}
|
|
|
|
numPoints = 0;
|
|
size = 0;
|
|
for (l = Extension->MountedDeviceList.Flink; l != &Extension->MountedDeviceList; l = l->Flink) {
|
|
deviceInfo = CONTAINING_RECORD(l, MOUNTED_DEVICE_INFORMATION, ListEntry);
|
|
if (UniqueId) {
|
|
if (UniqueId->UniqueIdLength == deviceInfo->UniqueId->UniqueIdLength) {
|
|
if (RtlCompareMemory(UniqueId->UniqueId, deviceInfo->UniqueId->UniqueId, UniqueId->UniqueIdLength) != UniqueId->UniqueIdLength) {
|
|
continue;
|
|
}
|
|
} else {
|
|
continue;
|
|
}
|
|
} else if (DeviceName) {
|
|
if (!RtlEqualUnicodeString(&targetName, &deviceInfo->DeviceName, TRUE)) {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
size += deviceInfo->UniqueId->UniqueIdLength;
|
|
size += deviceInfo->DeviceName.Length;
|
|
|
|
for (ll = deviceInfo->SymbolicLinkNames.Flink; ll != &deviceInfo->SymbolicLinkNames; ll = ll->Flink) {
|
|
symlinkEntry = CONTAINING_RECORD(ll, SYMBOLIC_LINK_NAME_ENTRY, ListEntry);
|
|
numPoints++;
|
|
size += symlinkEntry->SymbolicLinkName.Length;
|
|
}
|
|
|
|
if (UniqueId || DeviceName) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (UniqueId || DeviceName) {
|
|
if (l == &Extension->MountedDeviceList) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
|
|
irpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
output = Irp->AssociatedIrp.SystemBuffer;
|
|
output->Size = FIELD_OFFSET(MOUNTMGR_MOUNT_POINTS, MountPoints) + numPoints*sizeof(MOUNTMGR_MOUNT_POINT) + size;
|
|
output->NumberOfMountPoints = numPoints;
|
|
Irp->IoStatus.Information = output->Size;
|
|
if (output->Size > irpSp->Parameters.DeviceIoControl.OutputBufferLength) {
|
|
Irp->IoStatus.Information = sizeof(MOUNTMGR_MOUNT_POINTS);
|
|
if (DeviceName) {
|
|
ExFreePool(targetName.Buffer);
|
|
}
|
|
return STATUS_BUFFER_OVERFLOW;
|
|
}
|
|
|
|
numPoints = 0;
|
|
offset = output->Size - size;
|
|
for (l = Extension->MountedDeviceList.Flink; l != &Extension->MountedDeviceList; l = l->Flink) {
|
|
deviceInfo = CONTAINING_RECORD(l, MOUNTED_DEVICE_INFORMATION, ListEntry);
|
|
if (UniqueId) {
|
|
if (UniqueId->UniqueIdLength == deviceInfo->UniqueId->UniqueIdLength) {
|
|
if (RtlCompareMemory(UniqueId->UniqueId, deviceInfo->UniqueId->UniqueId, UniqueId->UniqueIdLength) != UniqueId->UniqueIdLength) {
|
|
continue;
|
|
}
|
|
} else {
|
|
continue;
|
|
}
|
|
} else if (DeviceName) {
|
|
if (!RtlEqualUnicodeString(&targetName, &deviceInfo->DeviceName, TRUE)) {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
uOffset = offset;
|
|
uLen = deviceInfo->UniqueId->UniqueIdLength;
|
|
dOffset = uOffset + uLen;
|
|
dLen = deviceInfo->DeviceName.Length;
|
|
offset += uLen + dLen;
|
|
|
|
RtlCopyMemory((PCHAR) output + uOffset, deviceInfo->UniqueId->UniqueId, uLen);
|
|
RtlCopyMemory((PCHAR) output + dOffset, deviceInfo->DeviceName.Buffer, dLen);
|
|
for (ll = deviceInfo->SymbolicLinkNames.Flink; ll != &deviceInfo->SymbolicLinkNames; ll = ll->Flink) {
|
|
symlinkEntry = CONTAINING_RECORD(ll, SYMBOLIC_LINK_NAME_ENTRY, ListEntry);
|
|
output->MountPoints[numPoints].SymbolicLinkNameOffset = offset;
|
|
output->MountPoints[numPoints].SymbolicLinkNameLength = symlinkEntry->SymbolicLinkName.Length;
|
|
if (symlinkEntry->IsInDatabase) {
|
|
output->MountPoints[numPoints].UniqueIdOffset = uOffset;
|
|
output->MountPoints[numPoints].UniqueIdLength = uLen;
|
|
} else {
|
|
output->MountPoints[numPoints].UniqueIdOffset = 0;
|
|
output->MountPoints[numPoints].UniqueIdLength = 0;
|
|
}
|
|
|
|
output->MountPoints[numPoints].DeviceNameOffset = dOffset;
|
|
output->MountPoints[numPoints].DeviceNameLength = dLen;
|
|
RtlCopyMemory((PCHAR) output + offset, symlinkEntry->SymbolicLinkName.Buffer, symlinkEntry->SymbolicLinkName.Length);
|
|
offset += symlinkEntry->SymbolicLinkName.Length;
|
|
numPoints++;
|
|
}
|
|
|
|
if (UniqueId || DeviceName) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (DeviceName) {
|
|
ExFreePool(targetName.Buffer);
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
MountMgrQueryPoints(
|
|
IN OUT PDEVICE_EXTENSION Extension,
|
|
IN OUT PIRP Irp
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
This routine queries a range of mount points.
|
|
Arguments:
|
|
Extension - Supplies the device extension.
|
|
Irp - Supplies the I/O request packet.
|
|
--*/
|
|
{
|
|
PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
PMOUNTMGR_MOUNT_POINT input;
|
|
ULONG len1, len2, len3, len;
|
|
UNICODE_STRING name;
|
|
NTSTATUS status;
|
|
PMOUNTDEV_UNIQUE_ID id;
|
|
|
|
if (irpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(MOUNTMGR_MOUNT_POINT)) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
input = Irp->AssociatedIrp.SystemBuffer;
|
|
if (!input->SymbolicLinkNameLength) {
|
|
input->SymbolicLinkNameOffset = 0;
|
|
}
|
|
if (!input->UniqueIdLength) {
|
|
input->UniqueIdOffset = 0;
|
|
}
|
|
if (!input->DeviceNameLength) {
|
|
input->DeviceNameOffset = 0;
|
|
}
|
|
len1 = input->SymbolicLinkNameOffset + input->SymbolicLinkNameLength;
|
|
len2 = input->UniqueIdOffset + input->UniqueIdLength;
|
|
len3 = input->DeviceNameOffset + input->DeviceNameLength;
|
|
len = len1 > len2 ? len1 : len2;
|
|
len = len > len3 ? len : len3;
|
|
if (len > irpSp->Parameters.DeviceIoControl.InputBufferLength) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
if (irpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(MOUNTMGR_MOUNT_POINTS)) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (input->SymbolicLinkNameLength) {
|
|
if (input->SymbolicLinkNameLength > 0xF000) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
name.Length = input->SymbolicLinkNameLength;
|
|
name.MaximumLength = name.Length + sizeof(WCHAR);
|
|
name.Buffer = ExAllocatePool(PagedPool, name.MaximumLength);
|
|
if (!name.Buffer) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
RtlCopyMemory(name.Buffer, (PCHAR) input + input->SymbolicLinkNameOffset, name.Length);
|
|
name.Buffer[name.Length/sizeof(WCHAR)] = 0;
|
|
status = QueryPointsFromSymbolicLinkName(Extension, &name, Irp);
|
|
ExFreePool(name.Buffer);
|
|
} else if (input->UniqueIdLength) {
|
|
id = ExAllocatePool(PagedPool, input->UniqueIdLength + sizeof(USHORT));
|
|
if (!id) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
id->UniqueIdLength = input->UniqueIdLength;
|
|
RtlCopyMemory(id->UniqueId, (PCHAR) input + input->UniqueIdOffset, input->UniqueIdLength);
|
|
status = QueryPointsFromMemory(Extension, Irp, id, NULL);
|
|
ExFreePool(id);
|
|
} else if (input->DeviceNameLength) {
|
|
if (input->DeviceNameLength > 0xF000) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
name.Length = input->DeviceNameLength;
|
|
name.MaximumLength = name.Length + sizeof(WCHAR);
|
|
name.Buffer = ExAllocatePool(PagedPool, name.MaximumLength);
|
|
if (!name.Buffer) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
RtlCopyMemory(name.Buffer, (PCHAR) input + input->DeviceNameOffset, name.Length);
|
|
name.Buffer[name.Length/sizeof(WCHAR)] = 0;
|
|
status = QueryPointsFromMemory(Extension, Irp, NULL, &name);
|
|
ExFreePool(name.Buffer);
|
|
} else {
|
|
status = QueryPointsFromMemory(Extension, Irp, NULL, NULL);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
VOID SendLinkDeleted(IN PUNICODE_STRING DeviceName, IN PUNICODE_STRING SymbolicLinkName)
|
|
/*++
|
|
Routine Description:
|
|
This routine alerts the mounted device that one of its links is being deleted.
|
|
Arguments:
|
|
DeviceName - Supplies the device name.
|
|
SymbolicLinkName - Supplies the symbolic link name being deleted.
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
PFILE_OBJECT fileObject;
|
|
PDEVICE_OBJECT deviceObject;
|
|
ULONG inputSize;
|
|
PMOUNTDEV_NAME input;
|
|
KEVENT event;
|
|
PIRP irp;
|
|
IO_STATUS_BLOCK ioStatus;
|
|
PIO_STACK_LOCATION irpSp;
|
|
|
|
status = IoGetDeviceObjectPointer(DeviceName, FILE_READ_ATTRIBUTES, &fileObject, &deviceObject);
|
|
if (!NT_SUCCESS(status)) {
|
|
return;
|
|
}
|
|
deviceObject = IoGetAttachedDeviceReference(fileObject->DeviceObject);
|
|
|
|
inputSize = sizeof(USHORT) + SymbolicLinkName->Length;
|
|
input = ExAllocatePool(PagedPool, inputSize);
|
|
if (!input) {
|
|
ObDereferenceObject(deviceObject);
|
|
ObDereferenceObject(fileObject);
|
|
return;
|
|
}
|
|
|
|
input->NameLength = SymbolicLinkName->Length;
|
|
RtlCopyMemory(input->Name, SymbolicLinkName->Buffer, SymbolicLinkName->Length);
|
|
|
|
KeInitializeEvent(&event, NotificationEvent, FALSE);
|
|
irp = IoBuildDeviceIoControlRequest(IOCTL_MOUNTDEV_LINK_DELETED, deviceObject, input, inputSize, NULL, 0, FALSE, &event, &ioStatus);
|
|
if (!irp) {
|
|
ObDereferenceObject(deviceObject);
|
|
ObDereferenceObject(fileObject);
|
|
return;
|
|
}
|
|
irpSp = IoGetNextIrpStackLocation(irp);
|
|
irpSp->FileObject = fileObject;
|
|
|
|
status = IoCallDriver(deviceObject, irp);
|
|
if (status == STATUS_PENDING) {
|
|
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
|
|
status = ioStatus.Status;
|
|
}
|
|
|
|
ObDereferenceObject(deviceObject);
|
|
ObDereferenceObject(fileObject);
|
|
}
|
|
|
|
|
|
VOID
|
|
DeleteSymbolicLinkNameFromMemory(
|
|
IN PDEVICE_EXTENSION Extension,
|
|
IN PUNICODE_STRING SymbolicLinkName,
|
|
IN BOOLEAN DbOnly
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
This routine deletes the given symbolic link name from memory.
|
|
Arguments:
|
|
Extension - Supplies the device extension.
|
|
SymbolicLinkName - Supplies the symbolic link name.
|
|
DbOnly - Supplies whether or not this is DBONLY.
|
|
--*/
|
|
{
|
|
PLIST_ENTRY l, ll;
|
|
PMOUNTED_DEVICE_INFORMATION deviceInfo;
|
|
PSYMBOLIC_LINK_NAME_ENTRY symlinkEntry;
|
|
|
|
for (l = Extension->MountedDeviceList.Flink; l != &Extension->MountedDeviceList; l = l->Flink) {
|
|
deviceInfo = CONTAINING_RECORD(l, MOUNTED_DEVICE_INFORMATION, ListEntry);
|
|
for (ll = deviceInfo->SymbolicLinkNames.Flink; ll != &deviceInfo->SymbolicLinkNames; ll = ll->Flink) {
|
|
symlinkEntry = CONTAINING_RECORD(ll, SYMBOLIC_LINK_NAME_ENTRY, ListEntry);
|
|
if (!RtlCompareUnicodeString(SymbolicLinkName, &symlinkEntry->SymbolicLinkName, TRUE)) {
|
|
if (DbOnly) {
|
|
symlinkEntry->IsInDatabase = FALSE;
|
|
} else {
|
|
SendLinkDeleted(&deviceInfo->NotificationName, SymbolicLinkName);
|
|
RemoveEntryList(ll);
|
|
ExFreePool(symlinkEntry->SymbolicLinkName.Buffer);
|
|
ExFreePool(symlinkEntry);
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
MountMgrDeletePoints(
|
|
IN OUT PDEVICE_EXTENSION Extension,
|
|
IN OUT PIRP Irp
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
This routine creates a mount point.
|
|
Arguments:
|
|
Extension - Supplies the device extension.
|
|
Irp - Supplies the I/O request packet.
|
|
Return Value:
|
|
NTSTATUS
|
|
--*/
|
|
{
|
|
PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
PMOUNTMGR_MOUNT_POINT point;
|
|
BOOLEAN singlePoint;
|
|
NTSTATUS status;
|
|
PMOUNTMGR_MOUNT_POINTS points;
|
|
ULONG i;
|
|
UNICODE_STRING symbolicLinkName;
|
|
PMOUNTDEV_UNIQUE_ID uniqueId;
|
|
UNICODE_STRING deviceName;
|
|
|
|
if (irpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(MOUNTMGR_MOUNT_POINT)) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
point = Irp->AssociatedIrp.SystemBuffer;
|
|
if (point->SymbolicLinkNameOffset && point->SymbolicLinkNameLength) {
|
|
singlePoint = TRUE;
|
|
} else {
|
|
singlePoint = FALSE;
|
|
}
|
|
|
|
status = MountMgrQueryPoints(Extension, Irp);
|
|
if (!NT_SUCCESS(status)) {
|
|
return status;
|
|
}
|
|
|
|
points = Irp->AssociatedIrp.SystemBuffer;
|
|
for (i = 0; i < points->NumberOfMountPoints; i++) {
|
|
symbolicLinkName.Length = points->MountPoints[i].SymbolicLinkNameLength;
|
|
symbolicLinkName.MaximumLength = symbolicLinkName.Length + sizeof(WCHAR);
|
|
symbolicLinkName.Buffer = ExAllocatePool(PagedPool, symbolicLinkName.MaximumLength);
|
|
if (!symbolicLinkName.Buffer) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
RtlCopyMemory(symbolicLinkName.Buffer, (PCHAR) points + points->MountPoints[i].SymbolicLinkNameOffset, symbolicLinkName.Length);
|
|
symbolicLinkName.Buffer[symbolicLinkName.Length/sizeof(WCHAR)] = 0;
|
|
if (singlePoint && IsDriveLetter(&symbolicLinkName)) {
|
|
uniqueId = ExAllocatePool(PagedPool, points->MountPoints[i].UniqueIdLength + sizeof(MOUNTDEV_UNIQUE_ID));
|
|
if (uniqueId) {
|
|
uniqueId->UniqueIdLength = points->MountPoints[i].UniqueIdLength;
|
|
RtlCopyMemory(uniqueId->UniqueId, (PCHAR) points + points->MountPoints[i].UniqueIdOffset, uniqueId->UniqueIdLength);
|
|
CreateNoDriveLetterEntry(uniqueId);
|
|
ExFreePool(uniqueId);
|
|
}
|
|
}
|
|
|
|
if (i == 0 && !singlePoint) {
|
|
uniqueId = ExAllocatePool(PagedPool, points->MountPoints[i].UniqueIdLength + sizeof(MOUNTDEV_UNIQUE_ID));
|
|
if (uniqueId) {
|
|
uniqueId->UniqueIdLength = points->MountPoints[i].UniqueIdLength;
|
|
RtlCopyMemory(uniqueId->UniqueId, (PCHAR) points + points->MountPoints[i].UniqueIdOffset, uniqueId->UniqueIdLength);
|
|
DeleteNoDriveLetterEntry(uniqueId);
|
|
ExFreePool(uniqueId);
|
|
}
|
|
}
|
|
|
|
IoDeleteSymbolicLink(&symbolicLinkName);
|
|
DeleteSymbolicLinkNameFromMemory(Extension, &symbolicLinkName, FALSE);
|
|
RtlDeleteRegistryValue(RTL_REGISTRY_ABSOLUTE, MOUNTED_DEVICES_KEY, symbolicLinkName.Buffer);
|
|
ExFreePool(symbolicLinkName.Buffer);
|
|
deviceName.Length = points->MountPoints[i].DeviceNameLength;
|
|
deviceName.MaximumLength = deviceName.Length;
|
|
deviceName.Buffer = (PWCHAR) ((PCHAR) points + points->MountPoints[i].DeviceNameOffset);
|
|
MountMgrNotifyNameChange(Extension, &deviceName, TRUE);
|
|
}
|
|
|
|
MountMgrNotify(Extension);
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
MountMgrDeletePointsDbOnly(
|
|
IN OUT PDEVICE_EXTENSION Extension,
|
|
IN OUT PIRP Irp
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
This routine deletes mount points from the database.
|
|
It does not delete the symbolic links or the in memory representation.
|
|
Arguments:
|
|
Extension - Supplies the device extension.
|
|
Irp - Supplies the I/O request packet.
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
PMOUNTMGR_MOUNT_POINTS points;
|
|
ULONG i;
|
|
UNICODE_STRING symbolicLinkName;
|
|
PMOUNTDEV_UNIQUE_ID uniqueId;
|
|
|
|
status = MountMgrQueryPoints(Extension, Irp);
|
|
if (!NT_SUCCESS(status)) {
|
|
return status;
|
|
}
|
|
|
|
points = Irp->AssociatedIrp.SystemBuffer;
|
|
for (i = 0; i < points->NumberOfMountPoints; i++) {
|
|
symbolicLinkName.Length = points->MountPoints[i].SymbolicLinkNameLength;
|
|
symbolicLinkName.MaximumLength = symbolicLinkName.Length + sizeof(WCHAR);
|
|
symbolicLinkName.Buffer = ExAllocatePool(PagedPool, symbolicLinkName.MaximumLength);
|
|
if (!symbolicLinkName.Buffer) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
RtlCopyMemory(symbolicLinkName.Buffer, (PCHAR) points + points->MountPoints[i].SymbolicLinkNameOffset, symbolicLinkName.Length);
|
|
symbolicLinkName.Buffer[symbolicLinkName.Length/sizeof(WCHAR)] = 0;
|
|
if (points->NumberOfMountPoints == 1 && IsDriveLetter(&symbolicLinkName)) {
|
|
uniqueId = ExAllocatePool(PagedPool, points->MountPoints[i].UniqueIdLength + sizeof(MOUNTDEV_UNIQUE_ID));
|
|
if (uniqueId) {
|
|
uniqueId->UniqueIdLength = points->MountPoints[i].UniqueIdLength;
|
|
RtlCopyMemory(uniqueId->UniqueId, (PCHAR) points + points->MountPoints[i].UniqueIdOffset, uniqueId->UniqueIdLength);
|
|
CreateNoDriveLetterEntry(uniqueId);
|
|
ExFreePool(uniqueId);
|
|
}
|
|
}
|
|
|
|
DeleteSymbolicLinkNameFromMemory(Extension, &symbolicLinkName, TRUE);
|
|
RtlDeleteRegistryValue(RTL_REGISTRY_ABSOLUTE, MOUNTED_DEVICES_KEY, symbolicLinkName.Buffer);
|
|
ExFreePool(symbolicLinkName.Buffer);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
VOID ProcessSuggestedDriveLetters(IN OUT PDEVICE_EXTENSION Extension)
|
|
/*++
|
|
Routine Description:
|
|
This routine processes the saved suggested drive letters.
|
|
Arguments:
|
|
Extension - Supplies the device extension.
|
|
--*/
|
|
{
|
|
PLIST_ENTRY l;
|
|
PMOUNTED_DEVICE_INFORMATION deviceInfo;
|
|
UNICODE_STRING symbolicLinkName;
|
|
WCHAR symNameBuffer[30];
|
|
|
|
for (l = Extension->MountedDeviceList.Flink; l != &Extension->MountedDeviceList; l = l->Flink) {
|
|
deviceInfo = CONTAINING_RECORD(l, MOUNTED_DEVICE_INFORMATION, ListEntry);
|
|
if (deviceInfo->SuggestedDriveLetter == 0xFF) {
|
|
if (!HasDriveLetter(deviceInfo) && !HasNoDriveLetterEntry(deviceInfo->UniqueId)) {
|
|
CreateNoDriveLetterEntry(deviceInfo->UniqueId);
|
|
}
|
|
|
|
deviceInfo->SuggestedDriveLetter = 0;
|
|
} else if (deviceInfo->SuggestedDriveLetter && !HasNoDriveLetterEntry(deviceInfo->UniqueId)) {
|
|
symbolicLinkName.Length = symbolicLinkName.MaximumLength = 28;
|
|
symbolicLinkName.Buffer = symNameBuffer;
|
|
RtlCopyMemory(symbolicLinkName.Buffer, L"\\DosDevices\\", 24);
|
|
symbolicLinkName.Buffer[12] = deviceInfo->SuggestedDriveLetter;
|
|
symbolicLinkName.Buffer[13] = ':';
|
|
MountMgrCreatePointWorker(Extension, &symbolicLinkName, &deviceInfo->DeviceName);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
BOOLEAN IsFtVolume(IN PUNICODE_STRING DeviceName)
|
|
/*++
|
|
Routine Description:
|
|
This routine checks to see if the given volume is an FT volume.
|
|
Arguments:
|
|
DeviceName - Supplies the device name.
|
|
Return Value:
|
|
FALSE - This is not an FT volume.
|
|
TRUE - This is an FT volume.
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
PFILE_OBJECT fileObject;
|
|
PDEVICE_OBJECT deviceObject, checkObject;
|
|
KEVENT event;
|
|
PIRP irp;
|
|
PARTITION_INFORMATION partInfo;
|
|
IO_STATUS_BLOCK ioStatus;
|
|
|
|
status = IoGetDeviceObjectPointer(DeviceName, FILE_READ_ATTRIBUTES, &fileObject, &deviceObject);
|
|
if (!NT_SUCCESS(status)) {
|
|
return FALSE;
|
|
}
|
|
checkObject = fileObject->DeviceObject;
|
|
deviceObject = IoGetAttachedDeviceReference(checkObject);
|
|
|
|
if (checkObject->Characteristics&FILE_REMOVABLE_MEDIA) {
|
|
ObDereferenceObject(deviceObject);
|
|
ObDereferenceObject(fileObject);
|
|
return FALSE;
|
|
}
|
|
|
|
ObDereferenceObject(fileObject);
|
|
|
|
KeInitializeEvent(&event, NotificationEvent, FALSE);
|
|
|
|
irp = IoBuildDeviceIoControlRequest(IOCTL_DISK_GET_PARTITION_INFO, deviceObject, NULL, 0, &partInfo, sizeof(partInfo), FALSE, &event, &ioStatus);
|
|
if (!irp) {
|
|
ObDereferenceObject(deviceObject);
|
|
return FALSE;
|
|
}
|
|
|
|
status = IoCallDriver(deviceObject, irp);
|
|
if (status == STATUS_PENDING) {
|
|
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
|
|
status = ioStatus.Status;
|
|
}
|
|
|
|
ObDereferenceObject(deviceObject);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (partInfo.PartitionType&PARTITION_NTFT) {
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
MountMgrNextDriveLetter(
|
|
IN OUT PDEVICE_EXTENSION Extension,
|
|
IN OUT PIRP Irp
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
This routine gives the next available drive letter to the given device unless the device already has a drive letter or the device has a flag specifying that it should not receive a drive letter.
|
|
Arguments:
|
|
Extension - Supplies the device extension.
|
|
Irp - Supplies the I/O request packet.
|
|
--*/
|
|
{
|
|
PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
PMOUNTMGR_DRIVE_LETTER_TARGET input;
|
|
UNICODE_STRING deviceName, targetName;
|
|
NTSTATUS status;
|
|
BOOLEAN isRecognized;
|
|
PLIST_ENTRY l;
|
|
PMOUNTED_DEVICE_INFORMATION deviceInfo;
|
|
PMOUNTMGR_DRIVE_LETTER_INFORMATION output;
|
|
PSYMBOLIC_LINK_NAME_ENTRY symlinkEntry;
|
|
UNICODE_STRING symbolicLinkName, floppyPrefix, cdromPrefix;
|
|
WCHAR symNameBuffer[30];
|
|
UCHAR startDriveLetterName;
|
|
|
|
if (irpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(MOUNTMGR_DRIVE_LETTER_TARGET) || irpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(MOUNTMGR_DRIVE_LETTER_INFORMATION)) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
input = Irp->AssociatedIrp.SystemBuffer;
|
|
if (input->DeviceNameLength + (ULONG) FIELD_OFFSET(MOUNTMGR_DRIVE_LETTER_TARGET, DeviceName) > irpSp->Parameters.DeviceIoControl.InputBufferLength) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (!Extension->SuggestedDriveLettersProcessed) {
|
|
ProcessSuggestedDriveLetters(Extension);
|
|
Extension->SuggestedDriveLettersProcessed = TRUE;
|
|
}
|
|
|
|
deviceName.MaximumLength = deviceName.Length = input->DeviceNameLength;
|
|
deviceName.Buffer = input->DeviceName;
|
|
|
|
status = QueryDeviceInformation(&deviceName, &targetName, NULL, NULL, &isRecognized);
|
|
if (!NT_SUCCESS(status)) {
|
|
return status;
|
|
}
|
|
|
|
for (l = Extension->MountedDeviceList.Flink; l != &Extension->MountedDeviceList; l = l->Flink) {
|
|
deviceInfo = CONTAINING_RECORD(l, MOUNTED_DEVICE_INFORMATION, ListEntry);
|
|
if (!RtlCompareUnicodeString(&targetName, &deviceInfo->DeviceName, TRUE)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (l == &Extension->MountedDeviceList) {
|
|
ExFreePool(targetName.Buffer);
|
|
return STATUS_OBJECT_NAME_NOT_FOUND;
|
|
}
|
|
|
|
output = Irp->AssociatedIrp.SystemBuffer;
|
|
output->DriveLetterWasAssigned = TRUE;
|
|
|
|
for (l = deviceInfo->SymbolicLinkNames.Flink; l != &deviceInfo->SymbolicLinkNames; l = l->Flink) {
|
|
symlinkEntry = CONTAINING_RECORD(l, SYMBOLIC_LINK_NAME_ENTRY, ListEntry);
|
|
if (IsDriveLetter(&symlinkEntry->SymbolicLinkName)) {
|
|
output->DriveLetterWasAssigned = FALSE;
|
|
output->CurrentDriveLetter = (UCHAR) symlinkEntry->SymbolicLinkName.Buffer[12];
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (l == &deviceInfo->SymbolicLinkNames && (!isRecognized || HasNoDriveLetterEntry(deviceInfo->UniqueId))) {
|
|
output->DriveLetterWasAssigned = FALSE;
|
|
output->CurrentDriveLetter = 0;
|
|
Irp->IoStatus.Information = sizeof(MOUNTMGR_DRIVE_LETTER_INFORMATION);
|
|
ExFreePool(targetName.Buffer);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
RtlInitUnicodeString(&floppyPrefix, L"\\Device\\Floppy");
|
|
RtlInitUnicodeString(&cdromPrefix, L"\\Device\\CdRom");
|
|
if (RtlPrefixUnicodeString(&floppyPrefix, &targetName, TRUE)) {
|
|
startDriveLetterName = 'A';
|
|
} else if (RtlPrefixUnicodeString(&cdromPrefix, &targetName, TRUE)) {
|
|
startDriveLetterName = 'D';
|
|
} else {
|
|
startDriveLetterName = 'C';
|
|
}
|
|
|
|
if (IsNEC_98) {
|
|
UNICODE_STRING StartDriveLetterFrom;
|
|
UNICODE_STRING defaultStartDriveLetter;
|
|
|
|
// Determin whether how to drive assign is NEC98 regacy (HD start is A:) or AT compatible (HD start C:).
|
|
RtlInitUnicodeString(&StartDriveLetterFrom, NULL);
|
|
RtlInitUnicodeString(&defaultStartDriveLetter, NULL);
|
|
|
|
{
|
|
RTL_QUERY_REGISTRY_TABLE SetupTypeTable[]=
|
|
{
|
|
{NULL,
|
|
RTL_QUERY_REGISTRY_DIRECT,
|
|
L"DriveLetter",
|
|
&StartDriveLetterFrom,
|
|
REG_SZ,
|
|
&defaultStartDriveLetter,
|
|
0
|
|
},
|
|
{NULL,0,NULL,NULL,REG_NONE,NULL,0}
|
|
};
|
|
|
|
status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE, L"\\Registry\\MACHINE\\SYSTEM\\Setup", SetupTypeTable, NULL, NULL);
|
|
if (!( NT_SUCCESS( status ) && ( (StartDriveLetterFrom.Buffer[0] == L'C') || (StartDriveLetterFrom.Buffer[0] == L'c')))){
|
|
startDriveLetterName = 'A';
|
|
}
|
|
}
|
|
|
|
RtlFreeUnicodeString(&StartDriveLetterFrom);
|
|
}
|
|
|
|
if (output->DriveLetterWasAssigned) {
|
|
ASSERT(deviceInfo->SuggestedDriveLetter != 0xFF);
|
|
if ((IsNEC_98? HasNoDriveLetterEntry(deviceInfo->UniqueId): !deviceInfo->SuggestedDriveLetter) && IsFtVolume(&deviceInfo->DeviceName)) {
|
|
output->DriveLetterWasAssigned = FALSE;
|
|
output->CurrentDriveLetter = 0;
|
|
Irp->IoStatus.Information = sizeof(MOUNTMGR_DRIVE_LETTER_INFORMATION);
|
|
ExFreePool(targetName.Buffer);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
symbolicLinkName.Length = symbolicLinkName.MaximumLength = 28;
|
|
symbolicLinkName.Buffer = symNameBuffer;
|
|
RtlCopyMemory(symbolicLinkName.Buffer, L"\\DosDevices\\", 24);
|
|
symbolicLinkName.Buffer[13] = ':';
|
|
|
|
if (deviceInfo->SuggestedDriveLetter) {
|
|
output->CurrentDriveLetter = deviceInfo->SuggestedDriveLetter;
|
|
symbolicLinkName.Buffer[12] = output->CurrentDriveLetter;
|
|
status = MountMgrCreatePointWorker(Extension, &symbolicLinkName, &targetName);
|
|
if (NT_SUCCESS(status)) {
|
|
Irp->IoStatus.Information = sizeof(MOUNTMGR_DRIVE_LETTER_INFORMATION);
|
|
ExFreePool(targetName.Buffer);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
for (output->CurrentDriveLetter = startDriveLetterName; output->CurrentDriveLetter <= 'Z'; output->CurrentDriveLetter++) {
|
|
symbolicLinkName.Buffer[12] = output->CurrentDriveLetter;
|
|
status = MountMgrCreatePointWorker(Extension, &symbolicLinkName, &targetName);
|
|
if (NT_SUCCESS(status)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (output->CurrentDriveLetter > 'Z') {
|
|
output->CurrentDriveLetter = 0;
|
|
output->DriveLetterWasAssigned = FALSE;
|
|
}
|
|
}
|
|
|
|
Irp->IoStatus.Information = sizeof(MOUNTMGR_DRIVE_LETTER_INFORMATION);
|
|
|
|
ExFreePool(targetName.Buffer);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
MountMgrVolumeMountPointCreated(
|
|
IN OUT PDEVICE_EXTENSION Extension,
|
|
IN OUT PIRP Irp,
|
|
IN NTSTATUS ResultOfWaitForDatabase
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
This routine alerts that mount manager that a volume mount point has been created so that the mount manager can replicate the database entry for the given mount point.
|
|
Arguments:
|
|
Extension - Supplies the device extension.
|
|
Irp - Supplies the I/O request packet.
|
|
--*/
|
|
{
|
|
PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
PMOUNTMGR_VOLUME_MOUNT_POINT input;
|
|
ULONG len1, len2, len;
|
|
UNICODE_STRING remoteDatabaseVolumeName;
|
|
HANDLE h;
|
|
UNICODE_STRING targetVolumeName, otherTargetVolumeName;
|
|
ULONG offset;
|
|
BOOLEAN entryFound;
|
|
PMOUNTMGR_FILE_ENTRY databaseEntry;
|
|
PMOUNTDEV_UNIQUE_ID uniqueId;
|
|
NTSTATUS status;
|
|
ULONG size;
|
|
UNICODE_STRING remoteDatabaseTargetName;
|
|
PLIST_ENTRY l;
|
|
PMOUNTED_DEVICE_INFORMATION deviceInfo;
|
|
PREPLICATED_UNIQUE_ID replUniqueId;
|
|
|
|
if (irpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(MOUNTMGR_VOLUME_MOUNT_POINT)) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
input = Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
len1 = input->SourceVolumeNameOffset + input->SourceVolumeNameLength;
|
|
len2 = input->TargetVolumeNameOffset + input->TargetVolumeNameLength;
|
|
len = len1 > len2 ? len1 : len2;
|
|
|
|
if (irpSp->Parameters.DeviceIoControl.InputBufferLength < len) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
remoteDatabaseVolumeName.Length = remoteDatabaseVolumeName.MaximumLength = input->SourceVolumeNameLength;
|
|
remoteDatabaseVolumeName.Buffer = (PWSTR) ((PCHAR) input + input->SourceVolumeNameOffset);
|
|
|
|
if (!NT_SUCCESS(ResultOfWaitForDatabase)) {
|
|
status = FindDeviceInfo(Extension, &remoteDatabaseVolumeName, FALSE, &deviceInfo);
|
|
if (NT_SUCCESS(status)) {
|
|
ReconcileThisDatabaseWithMaster(Extension, deviceInfo);
|
|
}
|
|
return status;
|
|
}
|
|
|
|
h = OpenRemoteDatabase(&remoteDatabaseVolumeName, TRUE);
|
|
if (!h) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
targetVolumeName.Length = targetVolumeName.MaximumLength = input->TargetVolumeNameLength;
|
|
targetVolumeName.Buffer = (PWSTR) ((PCHAR) input + input->TargetVolumeNameOffset);
|
|
|
|
offset = 0;
|
|
entryFound = FALSE;
|
|
for (;;) {
|
|
databaseEntry = GetRemoteDatabaseEntry(h, offset);
|
|
if (!databaseEntry) {
|
|
break;
|
|
}
|
|
|
|
otherTargetVolumeName.Length = otherTargetVolumeName.MaximumLength = databaseEntry->VolumeNameLength;
|
|
otherTargetVolumeName.Buffer = (PWSTR) ((PCHAR) databaseEntry + databaseEntry->VolumeNameOffset);
|
|
|
|
if (RtlEqualUnicodeString(&targetVolumeName, &otherTargetVolumeName, TRUE)) {
|
|
entryFound = TRUE;
|
|
break;
|
|
}
|
|
|
|
offset += databaseEntry->EntryLength;
|
|
ExFreePool(databaseEntry);
|
|
}
|
|
|
|
if (entryFound) {
|
|
databaseEntry->RefCount++;
|
|
status = WriteRemoteDatabaseEntry(h, offset, databaseEntry);
|
|
ExFreePool(databaseEntry);
|
|
} else {
|
|
status = QueryDeviceInformation(&targetVolumeName, NULL, &uniqueId, NULL, NULL);
|
|
if (!NT_SUCCESS(status)) {
|
|
CloseRemoteDatabase(h);
|
|
return status;
|
|
}
|
|
|
|
size = sizeof(MOUNTMGR_FILE_ENTRY) + targetVolumeName.Length + uniqueId->UniqueIdLength;
|
|
|
|
databaseEntry = ExAllocatePool(PagedPool, size);
|
|
if (!databaseEntry) {
|
|
ExFreePool(uniqueId);
|
|
CloseRemoteDatabase(h);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
databaseEntry->EntryLength = size;
|
|
databaseEntry->RefCount = 1;
|
|
databaseEntry->VolumeNameOffset = sizeof(MOUNTMGR_FILE_ENTRY);
|
|
databaseEntry->VolumeNameLength = targetVolumeName.Length;
|
|
databaseEntry->UniqueIdOffset = databaseEntry->VolumeNameOffset + databaseEntry->VolumeNameLength;
|
|
databaseEntry->UniqueIdLength = uniqueId->UniqueIdLength;
|
|
|
|
RtlCopyMemory((PCHAR) databaseEntry + databaseEntry->VolumeNameOffset, targetVolumeName.Buffer, databaseEntry->VolumeNameLength);
|
|
RtlCopyMemory((PCHAR) databaseEntry + databaseEntry->UniqueIdOffset, uniqueId->UniqueId, databaseEntry->UniqueIdLength);
|
|
|
|
status = AddRemoteDatabaseEntry(h, databaseEntry);
|
|
|
|
ExFreePool(databaseEntry);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
ExFreePool(uniqueId);
|
|
CloseRemoteDatabase(h);
|
|
return status;
|
|
}
|
|
|
|
status = QueryDeviceInformation(&remoteDatabaseVolumeName, &remoteDatabaseTargetName, NULL, NULL, NULL);
|
|
if (!NT_SUCCESS(status)) {
|
|
ExFreePool(uniqueId);
|
|
CloseRemoteDatabase(h);
|
|
return status;
|
|
}
|
|
|
|
for (l = Extension->MountedDeviceList.Flink; l != &Extension->MountedDeviceList; l = l->Flink) {
|
|
deviceInfo = CONTAINING_RECORD(l, MOUNTED_DEVICE_INFORMATION, ListEntry);
|
|
if (RtlEqualUnicodeString(&remoteDatabaseTargetName, &deviceInfo->DeviceName, TRUE)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
ExFreePool(remoteDatabaseTargetName.Buffer);
|
|
|
|
if (l == &Extension->MountedDeviceList) {
|
|
ExFreePool(uniqueId);
|
|
CloseRemoteDatabase(h);
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
replUniqueId = ExAllocatePool(PagedPool, sizeof(REPLICATED_UNIQUE_ID));
|
|
if (!replUniqueId) {
|
|
ExFreePool(uniqueId);
|
|
CloseRemoteDatabase(h);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
replUniqueId->UniqueId = uniqueId;
|
|
|
|
InsertTailList(&deviceInfo->ReplicatedUniqueIds, &replUniqueId->ListEntry);
|
|
}
|
|
|
|
CloseRemoteDatabase(h);
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
MountMgrVolumeMountPointDeleted(
|
|
IN OUT PDEVICE_EXTENSION Extension,
|
|
IN OUT PIRP Irp,
|
|
IN NTSTATUS ResultOfWaitForDatabase
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
This routine alerts that mount manager that a volume mount point has been created so that the mount manager can replicate the database entry for the given mount point.
|
|
Arguments:
|
|
Extension - Supplies the device extension.
|
|
Irp - Supplies the I/O request packet.
|
|
--*/
|
|
{
|
|
PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
PMOUNTMGR_VOLUME_MOUNT_POINT input;
|
|
ULONG len1, len2, len;
|
|
UNICODE_STRING remoteDatabaseVolumeName;
|
|
HANDLE h;
|
|
UNICODE_STRING targetVolumeName, otherTargetVolumeName;
|
|
ULONG offset;
|
|
BOOLEAN entryFound;
|
|
PMOUNTMGR_FILE_ENTRY databaseEntry;
|
|
NTSTATUS status;
|
|
UNICODE_STRING remoteDatabaseTargetName;
|
|
PLIST_ENTRY l;
|
|
PMOUNTED_DEVICE_INFORMATION deviceInfo;
|
|
PREPLICATED_UNIQUE_ID replUniqueId;
|
|
|
|
if (irpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(MOUNTMGR_VOLUME_MOUNT_POINT)) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
input = Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
len1 = input->SourceVolumeNameOffset + input->SourceVolumeNameLength;
|
|
len2 = input->TargetVolumeNameOffset + input->TargetVolumeNameLength;
|
|
len = len1 > len2 ? len1 : len2;
|
|
|
|
if (irpSp->Parameters.DeviceIoControl.InputBufferLength < len) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
remoteDatabaseVolumeName.Length = remoteDatabaseVolumeName.MaximumLength = input->SourceVolumeNameLength;
|
|
remoteDatabaseVolumeName.Buffer = (PWSTR) ((PCHAR) input + input->SourceVolumeNameOffset);
|
|
|
|
if (!NT_SUCCESS(ResultOfWaitForDatabase)) {
|
|
status = FindDeviceInfo(Extension, &remoteDatabaseVolumeName, FALSE, &deviceInfo);
|
|
if (NT_SUCCESS(status)) {
|
|
ReconcileThisDatabaseWithMaster(Extension, deviceInfo);
|
|
}
|
|
return status;
|
|
}
|
|
|
|
h = OpenRemoteDatabase(&remoteDatabaseVolumeName, TRUE);
|
|
if (!h) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
targetVolumeName.Length = targetVolumeName.MaximumLength = input->TargetVolumeNameLength;
|
|
targetVolumeName.Buffer = (PWSTR) ((PCHAR) input + input->TargetVolumeNameOffset);
|
|
|
|
offset = 0;
|
|
entryFound = FALSE;
|
|
for (;;) {
|
|
databaseEntry = GetRemoteDatabaseEntry(h, offset);
|
|
if (!databaseEntry) {
|
|
break;
|
|
}
|
|
|
|
otherTargetVolumeName.Length = otherTargetVolumeName.MaximumLength = databaseEntry->VolumeNameLength;
|
|
otherTargetVolumeName.Buffer = (PWSTR) ((PCHAR) databaseEntry + databaseEntry->VolumeNameOffset);
|
|
|
|
if (RtlEqualUnicodeString(&targetVolumeName, &otherTargetVolumeName, TRUE)) {
|
|
entryFound = TRUE;
|
|
break;
|
|
}
|
|
|
|
offset += databaseEntry->EntryLength;
|
|
ExFreePool(databaseEntry);
|
|
}
|
|
|
|
if (!entryFound) {
|
|
CloseRemoteDatabase(h);
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
databaseEntry->RefCount--;
|
|
if (databaseEntry->RefCount) {
|
|
status = WriteRemoteDatabaseEntry(h, offset, databaseEntry);
|
|
} else {
|
|
status = DeleteRemoteDatabaseEntry(h, offset);
|
|
if (!NT_SUCCESS(status)) {
|
|
ExFreePool(databaseEntry);
|
|
CloseRemoteDatabase(h);
|
|
return status;
|
|
}
|
|
|
|
status = QueryDeviceInformation(&remoteDatabaseVolumeName, &remoteDatabaseTargetName, NULL, NULL, NULL);
|
|
if (!NT_SUCCESS(status)) {
|
|
ExFreePool(databaseEntry);
|
|
CloseRemoteDatabase(h);
|
|
return status;
|
|
}
|
|
|
|
for (l = Extension->MountedDeviceList.Flink; l != &Extension->MountedDeviceList; l = l->Flink) {
|
|
deviceInfo = CONTAINING_RECORD(l, MOUNTED_DEVICE_INFORMATION, ListEntry);
|
|
if (RtlEqualUnicodeString(&remoteDatabaseTargetName, &deviceInfo->DeviceName, TRUE)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
ExFreePool(remoteDatabaseTargetName.Buffer);
|
|
|
|
if (l == &Extension->MountedDeviceList) {
|
|
ExFreePool(databaseEntry);
|
|
CloseRemoteDatabase(h);
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
for (l = deviceInfo->ReplicatedUniqueIds.Flink; l != &deviceInfo->ReplicatedUniqueIds; l = l->Flink) {
|
|
replUniqueId = CONTAINING_RECORD(l, REPLICATED_UNIQUE_ID, ListEntry);
|
|
if (replUniqueId->UniqueId->UniqueIdLength == databaseEntry->UniqueIdLength &&
|
|
RtlCompareMemory(replUniqueId->UniqueId->UniqueId, (PCHAR) databaseEntry + databaseEntry->UniqueIdOffset, databaseEntry->UniqueIdLength) == databaseEntry->UniqueIdLength) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (l == &deviceInfo->ReplicatedUniqueIds) {
|
|
ExFreePool(databaseEntry);
|
|
CloseRemoteDatabase(h);
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
RemoveEntryList(l);
|
|
ExFreePool(replUniqueId->UniqueId);
|
|
ExFreePool(replUniqueId);
|
|
}
|
|
|
|
ExFreePool(databaseEntry);
|
|
CloseRemoteDatabase(h);
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
NTSTATUS MountMgrKeepLinksWhenOffline(IN OUT PDEVICE_EXTENSION Extension, IN OUT PIRP Irp)
|
|
/*++
|
|
Routine Description:
|
|
This routine sets up the internal data structure to remember to keep the symbolic links for the given device even when the device goes offline.
|
|
Then when the device becomes on-line again, it is guaranteed that these links will be available and not taken by some other device.
|
|
Arguments:
|
|
Extension - Supplies the device extension.
|
|
Irp - Supplies the I/O request packet.
|
|
--*/
|
|
{
|
|
PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
PMOUNTMGR_TARGET_NAME input = Irp->AssociatedIrp.SystemBuffer;
|
|
ULONG size;
|
|
UNICODE_STRING deviceName;
|
|
NTSTATUS status;
|
|
PMOUNTED_DEVICE_INFORMATION deviceInfo;
|
|
|
|
if (irpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(MOUNTMGR_TARGET_NAME)) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
size = FIELD_OFFSET(MOUNTMGR_TARGET_NAME, DeviceName) + input->DeviceNameLength;
|
|
if (irpSp->Parameters.DeviceIoControl.InputBufferLength < size) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
deviceName.Length = deviceName.MaximumLength = input->DeviceNameLength;
|
|
deviceName.Buffer = input->DeviceName;
|
|
|
|
status = FindDeviceInfo(Extension, &deviceName, FALSE, &deviceInfo);
|
|
if (!NT_SUCCESS(status)) {
|
|
return status;
|
|
}
|
|
|
|
deviceInfo->KeepLinksWhenOffline = TRUE;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
VOID ReconcileAllDatabasesWithMaster(IN PDEVICE_EXTENSION Extension)
|
|
/*++
|
|
Routine Description:
|
|
This routine goes through all of the devices known to the MOUNTMGR and reconciles their database with the master database.
|
|
Arguments:
|
|
Extension - Supplies the device extension.
|
|
--*/
|
|
{
|
|
PLIST_ENTRY l;
|
|
PMOUNTED_DEVICE_INFORMATION deviceInfo;
|
|
OBJECT_ATTRIBUTES oa;
|
|
NTSTATUS status;
|
|
HANDLE handle;
|
|
IO_STATUS_BLOCK ioStatus;
|
|
|
|
for (l = Extension->MountedDeviceList.Flink; l != &Extension->MountedDeviceList; l = l->Flink) {
|
|
deviceInfo = CONTAINING_RECORD(l, MOUNTED_DEVICE_INFORMATION, ListEntry);
|
|
|
|
// Force an open of the volume so that the file system driver is loaded before AUTOCHK ties up the boot volume.
|
|
if (deviceInfo->IsRemovable) {
|
|
continue;
|
|
}
|
|
|
|
InitializeObjectAttributes(&oa, &deviceInfo->DeviceName, OBJ_CASE_INSENSITIVE, 0, 0);
|
|
status = ZwOpenFile(&handle, FILE_GENERIC_READ, &oa, &ioStatus, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_SYNCHRONOUS_IO_ALERT);
|
|
if (NT_SUCCESS(status)) {
|
|
ZwClose(handle);
|
|
}
|
|
|
|
ReconcileThisDatabaseWithMaster(Extension, deviceInfo);
|
|
}
|
|
}
|
|
|
|
|
|
NTSTATUS MountMgrCheckUnprocessedVolumes(IN OUT PDEVICE_EXTENSION Extension, IN OUT PIRP Irp)
|
|
/*++
|
|
Routine Description:
|
|
This routine sets up the internal data structure to remember to keep the symbolic links for the given device even when the device goes offline.
|
|
Then when the device becomes on-line again, it is guaranteed that these links will be available and not taken by some other device.
|
|
Arguments:
|
|
Extension - Supplies the device extension.
|
|
Irp - Supplies the I/O request packet.
|
|
--*/
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
LIST_ENTRY q;
|
|
PLIST_ENTRY l;
|
|
PMOUNTED_DEVICE_INFORMATION deviceInfo;
|
|
NTSTATUS status2;
|
|
|
|
if (IsListEmpty(&Extension->DeadMountedDeviceList)) {
|
|
KeReleaseSemaphore(&Extension->Mutex, IO_NO_INCREMENT, 1, FALSE);
|
|
return status;
|
|
}
|
|
|
|
q = Extension->DeadMountedDeviceList;
|
|
InitializeListHead(&Extension->DeadMountedDeviceList);
|
|
|
|
KeReleaseSemaphore(&Extension->Mutex, IO_NO_INCREMENT, 1, FALSE);
|
|
|
|
q.Blink->Flink = &q;
|
|
q.Flink->Blink = &q;
|
|
|
|
while (!IsListEmpty(&q)) {
|
|
l = RemoveHeadList(&q);
|
|
deviceInfo = CONTAINING_RECORD(l, MOUNTED_DEVICE_INFORMATION, ListEntry);
|
|
status2 = MountMgrMountedDeviceArrival(Extension, &deviceInfo->NotificationName, deviceInfo->NotAPdo);
|
|
ExFreePool(deviceInfo->NotificationName.Buffer);
|
|
ExFreePool(deviceInfo);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
status = status2;
|
|
}
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
NTSTATUS MountMgrVolumeArrivalNotification(IN OUT PDEVICE_EXTENSION Extension, IN OUT PIRP Irp)
|
|
/*++
|
|
Routine Description:
|
|
This routine performs the same actions as though PNP had notified the mount manager of a new volume arrival.
|
|
Arguments:
|
|
Extension - Supplies the device extension.
|
|
Irp - Supplies the I/O request packet.
|
|
--*/
|
|
{
|
|
PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
PMOUNTMGR_TARGET_NAME input = Irp->AssociatedIrp.SystemBuffer;
|
|
ULONG size;
|
|
UNICODE_STRING deviceName;
|
|
BOOLEAN oldHardErrorMode;
|
|
NTSTATUS status;
|
|
|
|
if (irpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(MOUNTMGR_TARGET_NAME)) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
size = FIELD_OFFSET(MOUNTMGR_TARGET_NAME, DeviceName) + input->DeviceNameLength;
|
|
if (irpSp->Parameters.DeviceIoControl.InputBufferLength < size) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
deviceName.Length = deviceName.MaximumLength = input->DeviceNameLength;
|
|
deviceName.Buffer = input->DeviceName;
|
|
|
|
oldHardErrorMode = PsGetCurrentThread()->HardErrorsAreDisabled;
|
|
PsGetCurrentThread()->HardErrorsAreDisabled = TRUE;
|
|
|
|
status = MountMgrMountedDeviceArrival(Extension, &deviceName, TRUE);
|
|
PsGetCurrentThread()->HardErrorsAreDisabled = oldHardErrorMode;
|
|
return status;
|
|
}
|
|
|
|
|
|
NTSTATUS MountMgrDeviceControl(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
|
|
/*++
|
|
Routine Description:
|
|
This routine is the dispatch for a device io control request.
|
|
Arguments:
|
|
DeviceObject - Supplies the device object.
|
|
Irp - Supplies the I/O request packet.
|
|
--*/
|
|
{
|
|
PDEVICE_EXTENSION extension = DeviceObject->DeviceExtension;
|
|
PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
NTSTATUS status, status2;
|
|
PMOUNTED_DEVICE_INFORMATION deviceInfo;
|
|
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
KeWaitForSingleObject(&extension->Mutex, Executive, KernelMode, FALSE, NULL);
|
|
|
|
switch (irpSp->Parameters.DeviceIoControl.IoControlCode) {
|
|
case IOCTL_MOUNTMGR_CREATE_POINT:
|
|
status = MountMgrCreatePoint(extension, Irp);
|
|
break;
|
|
case IOCTL_MOUNTMGR_QUERY_POINTS_ADMIN:
|
|
case IOCTL_MOUNTMGR_QUERY_POINTS:
|
|
status = MountMgrQueryPoints(extension, Irp);
|
|
break;
|
|
case IOCTL_MOUNTMGR_DELETE_POINTS:
|
|
status = MountMgrDeletePoints(extension, Irp);
|
|
break;
|
|
case IOCTL_MOUNTMGR_DELETE_POINTS_DBONLY:
|
|
status = MountMgrDeletePointsDbOnly(extension, Irp);
|
|
break;
|
|
case IOCTL_MOUNTMGR_NEXT_DRIVE_LETTER:
|
|
status = MountMgrNextDriveLetter(extension, Irp);
|
|
break;
|
|
case IOCTL_MOUNTMGR_AUTO_DL_ASSIGNMENTS:
|
|
extension->AutomaticDriveLetterAssignment = TRUE;
|
|
ReconcileAllDatabasesWithMaster(extension);
|
|
status = STATUS_SUCCESS;
|
|
break;
|
|
case IOCTL_MOUNTMGR_VOLUME_MOUNT_POINT_CREATED:
|
|
KeReleaseSemaphore(&extension->Mutex, IO_NO_INCREMENT, 1, FALSE);
|
|
status2 = WaitForRemoteDatabaseSemaphore(extension);
|
|
KeWaitForSingleObject(&extension->Mutex, Executive, KernelMode, FALSE, NULL);
|
|
status = MountMgrVolumeMountPointCreated(extension, Irp, status2);
|
|
if (NT_SUCCESS(status2)) {
|
|
ReleaseRemoteDatabaseSemaphore(extension);
|
|
}
|
|
break;
|
|
case IOCTL_MOUNTMGR_VOLUME_MOUNT_POINT_DELETED:
|
|
KeReleaseSemaphore(&extension->Mutex, IO_NO_INCREMENT, 1, FALSE);
|
|
status2 = WaitForRemoteDatabaseSemaphore(extension);
|
|
KeWaitForSingleObject(&extension->Mutex, Executive, KernelMode, FALSE, NULL);
|
|
status = MountMgrVolumeMountPointDeleted(extension, Irp, status2);
|
|
if (NT_SUCCESS(status2)) {
|
|
ReleaseRemoteDatabaseSemaphore(extension);
|
|
}
|
|
break;
|
|
case IOCTL_MOUNTMGR_CHANGE_NOTIFY:
|
|
status = MountMgrChangeNotify(extension, Irp);
|
|
break;
|
|
case IOCTL_MOUNTMGR_KEEP_LINKS_WHEN_OFFLINE:
|
|
status = MountMgrKeepLinksWhenOffline(extension, Irp);
|
|
break;
|
|
case IOCTL_MOUNTMGR_CHECK_UNPROCESSED_VOLUMES:
|
|
status = MountMgrCheckUnprocessedVolumes(extension, Irp);
|
|
Irp->IoStatus.Status = status;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return status;
|
|
case IOCTL_MOUNTMGR_VOLUME_ARRIVAL_NOTIFICATION:
|
|
KeReleaseSemaphore(&extension->Mutex, IO_NO_INCREMENT, 1, FALSE);
|
|
status = MountMgrVolumeArrivalNotification(extension, Irp);
|
|
Irp->IoStatus.Status = status;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return status;
|
|
default:
|
|
status = STATUS_INVALID_DEVICE_REQUEST;
|
|
break;
|
|
}
|
|
|
|
KeReleaseSemaphore(&extension->Mutex, IO_NO_INCREMENT, 1, FALSE);
|
|
|
|
if (status != STATUS_PENDING) {
|
|
Irp->IoStatus.Status = status;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma code_seg()
|
|
#endif
|
|
|
|
|
|
VOID WorkerThread(IN PVOID Extension)
|
|
/*++
|
|
Routine Description:
|
|
This is a worker thread to process work queue items.
|
|
Arguments:
|
|
Extension - Supplies the device extension.
|
|
--*/
|
|
{
|
|
PDEVICE_EXTENSION extension = Extension;
|
|
UNICODE_STRING volumeSafeEventName;
|
|
OBJECT_ATTRIBUTES oa;
|
|
KEVENT event;
|
|
LARGE_INTEGER timeout;
|
|
ULONG i;
|
|
NTSTATUS status;
|
|
HANDLE volumeSafeEvent;
|
|
KIRQL irql;
|
|
PLIST_ENTRY l;
|
|
PWORK_QUEUE_ITEM queueItem;
|
|
|
|
RtlInitUnicodeString(&volumeSafeEventName, L"\\Device\\VolumesSafeForWriteAccess");
|
|
InitializeObjectAttributes(&oa, &volumeSafeEventName, OBJ_CASE_INSENSITIVE, NULL, NULL);
|
|
KeInitializeEvent(&event, NotificationEvent, FALSE);
|
|
timeout.QuadPart = -10*1000*1000; // 1 second
|
|
|
|
for (i = 0; i < 1000; i++) {
|
|
status = ZwOpenEvent(&volumeSafeEvent, EVENT_ALL_ACCESS, &oa);
|
|
if (NT_SUCCESS(status)) {
|
|
break;
|
|
}
|
|
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, &timeout);
|
|
}
|
|
|
|
if (i < 1000) {
|
|
ZwWaitForSingleObject(volumeSafeEvent, FALSE, NULL);
|
|
ZwClose(volumeSafeEvent);
|
|
}
|
|
|
|
for (;;) {
|
|
KeWaitForSingleObject(&extension->WorkerSemaphore, Executive, KernelMode, FALSE, NULL);
|
|
|
|
KeAcquireSpinLock(&extension->WorkerSpinLock, &irql);
|
|
ASSERT(!IsListEmpty(&extension->WorkerQueue));
|
|
l = RemoveHeadList(&extension->WorkerQueue);
|
|
KeReleaseSpinLock(&extension->WorkerSpinLock, irql);
|
|
|
|
queueItem = CONTAINING_RECORD(l, WORK_QUEUE_ITEM, List);
|
|
queueItem->WorkerRoutine(queueItem->Parameter);
|
|
|
|
ExFreePool(queueItem);
|
|
|
|
if (InterlockedDecrement(&extension->WorkerRefCount) < 0) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
PsTerminateSystemThread(STATUS_SUCCESS);
|
|
}
|
|
|
|
|
|
NTSTATUS QueueWorkItem(IN PDEVICE_EXTENSION Extension, IN PWORK_QUEUE_ITEM WorkItem)
|
|
/*++
|
|
Routine Description:
|
|
This routine queues the given work item to the worker thread and if necessary starts the worker thread.
|
|
Arguments:
|
|
Extension - Supplies the device extension.
|
|
WorkItem - Supplies the work item to be queued.
|
|
--*/
|
|
{
|
|
OBJECT_ATTRIBUTES oa;
|
|
NTSTATUS status;
|
|
HANDLE handle;
|
|
KIRQL irql;
|
|
|
|
if (!InterlockedIncrement(&Extension->WorkerRefCount)) {
|
|
InitializeObjectAttributes(&oa, NULL, OBJ_KERNEL_HANDLE, NULL, NULL);
|
|
status = PsCreateSystemThread(&handle, 0, &oa, 0, NULL, WorkerThread, Extension);
|
|
if (!NT_SUCCESS(status)) {
|
|
return status;
|
|
}
|
|
|
|
ZwClose(handle);
|
|
}
|
|
|
|
KeAcquireSpinLock(&Extension->WorkerSpinLock, &irql);
|
|
InsertTailList(&Extension->WorkerQueue, &WorkItem->List);
|
|
KeReleaseSpinLock(&Extension->WorkerSpinLock, irql);
|
|
|
|
KeReleaseSemaphore(&Extension->WorkerSemaphore, 0, 1, FALSE);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
VOID MountMgrNotify(IN PDEVICE_EXTENSION Extension)
|
|
/*++
|
|
Routine Description:
|
|
This routine completes all of the change notify irps in the queue.
|
|
Arguments:
|
|
Extension - Supplies the device extension.
|
|
--*/
|
|
{
|
|
LIST_ENTRY q;
|
|
KIRQL irql;
|
|
PLIST_ENTRY p;
|
|
PIRP irp;
|
|
PMOUNTMGR_CHANGE_NOTIFY_INFO output;
|
|
|
|
Extension->EpicNumber++;
|
|
|
|
InitializeListHead(&q);
|
|
IoAcquireCancelSpinLock(&irql);
|
|
while (!IsListEmpty(&Extension->ChangeNotifyIrps)) {
|
|
p = RemoveHeadList(&Extension->ChangeNotifyIrps);
|
|
irp = CONTAINING_RECORD(p, IRP, Tail.Overlay.ListEntry);
|
|
IoSetCancelRoutine(irp, NULL);
|
|
InsertTailList(&q, p);
|
|
}
|
|
IoReleaseCancelSpinLock(irql);
|
|
|
|
while (!IsListEmpty(&q)) {
|
|
p = RemoveHeadList(&q);
|
|
irp = CONTAINING_RECORD(p, IRP, Tail.Overlay.ListEntry);
|
|
output = irp->AssociatedIrp.SystemBuffer;
|
|
output->EpicNumber = Extension->EpicNumber;
|
|
irp->IoStatus.Information = sizeof(MOUNTMGR_CHANGE_NOTIFY_INFO);
|
|
IoCompleteRequest(irp, IO_NO_INCREMENT);
|
|
}
|
|
}
|
|
|
|
|
|
VOID MountMgrCancel(IN OUT PDEVICE_OBJECT DeviceObject, IN OUT PIRP Irp)
|
|
/*++
|
|
Routine Description:
|
|
This routine is called on when the given IRP is cancelled.
|
|
It will dequeue this IRP off the work queue and complete the request as CANCELLED.
|
|
Arguments:
|
|
DeviceObject - Supplies the device object.
|
|
Irp - Supplies the IRP.
|
|
--*/
|
|
{
|
|
RemoveEntryList(&Irp->Tail.Overlay.ListEntry);
|
|
IoReleaseCancelSpinLock(Irp->CancelIrql);
|
|
|
|
Irp->IoStatus.Status = STATUS_CANCELLED;
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
}
|
|
|
|
|
|
NTSTATUS MountMgrChangeNotify(IN OUT PDEVICE_EXTENSION Extension, IN OUT PIRP Irp)
|
|
/*++
|
|
Routine Description:
|
|
This routine returns when the current Epic number is different than the one given.
|
|
Arguments:
|
|
Extension - Supplies the device extension.
|
|
Irp - Supplies the I/O request packet.
|
|
Return Value:
|
|
NTSTATUS
|
|
--*/
|
|
{
|
|
PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
PMOUNTMGR_CHANGE_NOTIFY_INFO input;
|
|
KIRQL irql;
|
|
|
|
if (irpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(MOUNTMGR_CHANGE_NOTIFY_INFO) ||
|
|
irpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(MOUNTMGR_CHANGE_NOTIFY_INFO)) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
input = Irp->AssociatedIrp.SystemBuffer;
|
|
if (input->EpicNumber != Extension->EpicNumber) {
|
|
input->EpicNumber = Extension->EpicNumber;
|
|
Irp->IoStatus.Information = sizeof(MOUNTMGR_CHANGE_NOTIFY_INFO);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
IoAcquireCancelSpinLock(&irql);
|
|
if (Irp->Cancel) {
|
|
IoReleaseCancelSpinLock(irql);
|
|
return STATUS_CANCELLED;
|
|
}
|
|
|
|
InsertTailList(&Extension->ChangeNotifyIrps, &Irp->Tail.Overlay.ListEntry);
|
|
IoMarkIrpPending(Irp);
|
|
IoSetCancelRoutine(Irp, MountMgrCancel);
|
|
IoReleaseCancelSpinLock(irql);
|
|
return STATUS_PENDING;
|
|
}
|
|
|
|
|
|
NTSTATUS UniqueIdChangeNotifyCompletion(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID WorkItem)
|
|
/*++
|
|
Routine Description:
|
|
Completion routine for a change notify.
|
|
Arguments:
|
|
DeviceObject - Not used.
|
|
Irp - Supplies the IRP.
|
|
Extension - Supplies the work item.
|
|
Return Value:
|
|
STATUS_MORE_PROCESSING_REQUIRED
|
|
--*/
|
|
{
|
|
PCHANGE_NOTIFY_WORK_ITEM workItem = WorkItem;
|
|
|
|
if (!NT_SUCCESS(Irp->IoStatus.Status)) {
|
|
IoFreeIrp(workItem->Irp);
|
|
ExFreePool(workItem->DeviceName.Buffer);
|
|
ExFreePool(workItem->SystemBuffer);
|
|
ExFreePool(workItem);
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
}
|
|
|
|
ExInitializeWorkItem(&workItem->WorkItem, UniqueIdChangeNotifyWorker, workItem);
|
|
ExQueueWorkItem(&workItem->WorkItem, DelayedWorkQueue);
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
}
|
|
|
|
|
|
NTSTATUS MountMgrCleanup(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
|
|
/*++
|
|
Routine Description:
|
|
This routine cancels all of the IRPs currently queued on the given device.
|
|
Arguments:
|
|
DeviceObject - Supplies the device object.
|
|
Irp - Supplies the cleanup IRP.
|
|
Return Value:
|
|
STATUS_SUCCESS - Success.
|
|
--*/
|
|
{
|
|
PDEVICE_EXTENSION Extension = DeviceObject->DeviceExtension;
|
|
PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
PFILE_OBJECT file = irpSp->FileObject;
|
|
KIRQL irql;
|
|
PLIST_ENTRY l;
|
|
PIRP irp;
|
|
|
|
IoAcquireCancelSpinLock(&irql);
|
|
|
|
for (;;) {
|
|
for (l = Extension->ChangeNotifyIrps.Flink; l != &Extension->ChangeNotifyIrps; l = l->Flink) {
|
|
irp = CONTAINING_RECORD(l, IRP, Tail.Overlay.ListEntry);
|
|
if (IoGetCurrentIrpStackLocation(irp)->FileObject == file) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (l == &Extension->ChangeNotifyIrps) {
|
|
break;
|
|
}
|
|
|
|
irp->Cancel = TRUE;
|
|
irp->CancelIrql = irql;
|
|
irp->CancelRoutine = NULL;
|
|
MountMgrCancel(DeviceObject, irp);
|
|
|
|
IoAcquireCancelSpinLock(&irql);
|
|
}
|
|
|
|
IoReleaseCancelSpinLock(irql);
|
|
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
Irp->IoStatus.Information = 0;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath)
|
|
/*++
|
|
Routine Description:
|
|
This routine is the entry point for the driver.
|
|
Arguments:
|
|
DriverObject - Supplies the driver object.
|
|
RegistryPath - Supplies the registry path for this driver.
|
|
Return Value:
|
|
NTSTATUS
|
|
--*/
|
|
{
|
|
UNICODE_STRING deviceName, symbolicLinkName;
|
|
NTSTATUS status;
|
|
PDEVICE_OBJECT deviceObject;
|
|
PDEVICE_EXTENSION extension;
|
|
|
|
RtlCreateRegistryKey(RTL_REGISTRY_ABSOLUTE, MOUNTED_DEVICES_KEY);
|
|
|
|
RtlInitUnicodeString(&deviceName, MOUNTMGR_DEVICE_NAME);
|
|
status = IoCreateDevice(DriverObject, sizeof(DEVICE_EXTENSION), &deviceName, FILE_DEVICE_NETWORK, 0, FALSE, &deviceObject);
|
|
if (!NT_SUCCESS(status)) {
|
|
return status;
|
|
}
|
|
|
|
extension = deviceObject->DeviceExtension;
|
|
RtlZeroMemory(extension, sizeof(DEVICE_EXTENSION));
|
|
extension->DeviceObject = deviceObject;
|
|
extension->DriverObject = DriverObject;
|
|
InitializeListHead(&extension->MountedDeviceList);
|
|
InitializeListHead(&extension->DeadMountedDeviceList);
|
|
KeInitializeSemaphore(&extension->Mutex, 1, 1);
|
|
KeInitializeSemaphore(&extension->RemoteDatabaseSemaphore, 1, 1);
|
|
InitializeListHead(&extension->ChangeNotifyIrps);
|
|
extension->EpicNumber = 1;
|
|
InitializeListHead(&extension->SavedLinksList);
|
|
InitializeListHead(&extension->WorkerQueue);
|
|
KeInitializeSemaphore(&extension->WorkerSemaphore, 0, MAXLONG);
|
|
extension->WorkerRefCount = -1;
|
|
KeInitializeSpinLock(&extension->WorkerSpinLock);
|
|
|
|
RtlInitUnicodeString(&symbolicLinkName, L"\\DosDevices\\MountPointManager");
|
|
IoCreateSymbolicLink(&symbolicLinkName, &deviceName);
|
|
|
|
ObReferenceObject(DriverObject);
|
|
|
|
status = IoRegisterPlugPlayNotification(
|
|
EventCategoryDeviceInterfaceChange,
|
|
PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES,
|
|
(PVOID) &MOUNTDEV_MOUNTED_DEVICE_GUID,
|
|
DriverObject,
|
|
MountMgrMountedDeviceNotification,
|
|
extension,
|
|
&extension->NotificationEntry);
|
|
if (!NT_SUCCESS(status)) {
|
|
ObDereferenceObject(DriverObject);
|
|
return status;
|
|
}
|
|
|
|
DriverObject->MajorFunction[IRP_MJ_CREATE] = MountMgrCreateClose;
|
|
DriverObject->MajorFunction[IRP_MJ_CLOSE] = MountMgrCreateClose;
|
|
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = MountMgrDeviceControl;
|
|
DriverObject->MajorFunction[IRP_MJ_CLEANUP] = MountMgrCleanup;
|
|
|
|
return STATUS_SUCCESS;
|
|
} |