9900 lines
292 KiB
C
9900 lines
292 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
|
||
|
||
Notes:
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#define _NTSRV_
|
||
|
||
#include <ntosp.h>
|
||
#include <zwapi.h>
|
||
#include <initguid.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>
|
||
|
||
|
||
// The protection on these IOCTLs have been updated. Keep the old definitions so we can send updates to
|
||
// anyone who has yet to recompile with the new definitions.
|
||
//
|
||
#define IOCTL_MOUNTDEV_LINK_CREATED_OBSOLETE CTL_CODE (MOUNTDEVCONTROLTYPE, 4, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||
#define IOCTL_MOUNTDEV_LINK_DELETED_OBSOLETE CTL_CODE (MOUNTDEVCONTROLTYPE, 5, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||
|
||
#define DeclareStaticUnicodeString(_StringName, _StringValue) \
|
||
static UNICODE_STRING (_StringName) = {sizeof (_StringValue) - sizeof (UNICODE_NULL), \
|
||
sizeof (_StringValue), \
|
||
(_StringValue)}
|
||
|
||
|
||
// NOTE, this structure is here because it was not defined in NTIOAPI.H.
|
||
// This should be taken out in the future.
|
||
// This is stolen from NTFS.H
|
||
|
||
typedef struct _REPARSE_INDEX_KEY {
|
||
|
||
//
|
||
// The tag of the reparse point.
|
||
//
|
||
|
||
ULONG FileReparseTag;
|
||
|
||
//
|
||
// The file record Id where the reparse point is set.
|
||
//
|
||
|
||
LARGE_INTEGER FileId;
|
||
|
||
} REPARSE_INDEX_KEY, *PREPARSE_INDEX_KEY;
|
||
|
||
|
||
|
||
#define MAX(_a, _b) ((_a) > (_b) ? (_a) : (_b))
|
||
#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
|
||
MountMgrMountedDeviceRemoval(
|
||
IN PDEVICE_EXTENSION Extension,
|
||
IN PUNICODE_STRING NotificationName
|
||
);
|
||
|
||
VOID
|
||
MountMgrUnload(
|
||
IN PDRIVER_OBJECT DriverObject
|
||
);
|
||
|
||
ULONG
|
||
MountmgrReadNoAutoMount(
|
||
IN PUNICODE_STRING RegistryPath
|
||
);
|
||
|
||
typedef struct _RECONCILE_WORK_ITEM_INFO {
|
||
PDEVICE_EXTENSION Extension;
|
||
PMOUNTED_DEVICE_INFORMATION DeviceInfo;
|
||
} RECONCILE_WORK_ITEM_INFO, *PRECONCILE_WORK_ITEM_INFO;
|
||
|
||
typedef VOID (*PRECONCILE_WRKRTN) (
|
||
IN PVOID WorkItem
|
||
);
|
||
|
||
typedef struct _RECONCILE_WORK_ITEM {
|
||
LIST_ENTRY List;
|
||
PIO_WORKITEM WorkItem;
|
||
PRECONCILE_WRKRTN WorkerRoutine;
|
||
PVOID Parameter;
|
||
RECONCILE_WORK_ITEM_INFO WorkItemInfo;
|
||
} RECONCILE_WORK_ITEM, *PRECONCILE_WORK_ITEM;
|
||
|
||
NTSTATUS
|
||
QueueWorkItem(
|
||
IN PDEVICE_EXTENSION Extension,
|
||
IN PRECONCILE_WORK_ITEM WorkItem,
|
||
IN PVOID Parameter
|
||
);
|
||
|
||
VOID
|
||
SendOnlineNotification(
|
||
IN PUNICODE_STRING NotificationName
|
||
);
|
||
|
||
|
||
#ifdef POOL_TAGGING
|
||
#undef ExAllocatePool
|
||
|
||
#define ExAllocatePool(_a,_b) ExAllocatePoolWithTag((_a), (_b), MOUNTMGR_TAG_MISC)
|
||
|
||
#define MOUNTMGR_TAG_MISC 'AtnM' // MntA
|
||
#define MOUNTMGR_TAG_BUFFER 'BtnM' // MntB
|
||
|
||
#endif
|
||
|
||
//
|
||
// Globals
|
||
//
|
||
PDEVICE_OBJECT gdeviceObject = NULL;
|
||
KEVENT UnloadEvent;
|
||
LONG Unloading = 0;
|
||
|
||
DeclareStaticUnicodeString (DeviceName, MOUNTMGR_DEVICE_NAME);
|
||
DeclareStaticUnicodeString (DeviceSymbolicLinkName, L"\\DosDevices\\MountPointManager");
|
||
DeclareStaticUnicodeString (DosDevices, L"\\DosDevices\\");
|
||
DeclareStaticUnicodeString (DosPrefix, L"\\??\\");
|
||
DeclareStaticUnicodeString (GlobalPrefix, L"\\GLOBAL??\\");
|
||
DeclareStaticUnicodeString (VolumeNamePrefix, L"\\??\\Volume");
|
||
DeclareStaticUnicodeString (DeviceFloppy, L"\\Device\\Floppy");
|
||
DeclareStaticUnicodeString (DeviceCdRom, L"\\Device\\CdRom");
|
||
|
||
DeclareStaticUnicodeString (VolumeSafeEventName, L"\\Device\\VolumesSafeForWriteAccess");
|
||
|
||
DeclareStaticUnicodeString (ReparseIndexName, L"\\$Extend\\$Reparse:$R:$INDEX_ALLOCATION");
|
||
DeclareStaticUnicodeString (RemoteDatabaseFileName, L"\\System Volume Information\\MountPointManagerRemoteDatabase");
|
||
DeclareStaticUnicodeString (RemoteDatabaseFileNameLegacy, L"\\:$MountMgrRemoteDatabase");
|
||
|
||
|
||
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text(INIT, DriverEntry)
|
||
#pragma alloc_text(INIT, MountmgrReadNoAutoMount)
|
||
#pragma alloc_text(PAGE, MountMgrUnload)
|
||
#endif
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma code_seg("PAGE")
|
||
#endif
|
||
|
||
|
||
NTSTATUS
|
||
CreateStringWithGlobal(
|
||
IN PUNICODE_STRING SymbolicLinkName,
|
||
OUT PUNICODE_STRING StringWithGlobal
|
||
)
|
||
|
||
{
|
||
UNICODE_STRING newSource;
|
||
|
||
|
||
if (RtlPrefixUnicodeString(&DosDevices, SymbolicLinkName, TRUE)) {
|
||
|
||
newSource.Length = SymbolicLinkName->Length + GlobalPrefix.Length - DosDevices.Length;
|
||
newSource.MaximumLength = newSource.Length + sizeof(WCHAR);
|
||
newSource.Buffer = ExAllocatePool(PagedPool, newSource.MaximumLength);
|
||
if (!newSource.Buffer) {
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
RtlCopyMemory(newSource.Buffer, GlobalPrefix.Buffer, GlobalPrefix.Length);
|
||
RtlCopyMemory((PCHAR) newSource.Buffer + GlobalPrefix.Length,
|
||
(PCHAR) SymbolicLinkName->Buffer + DosDevices.Length,
|
||
SymbolicLinkName->Length - DosDevices.Length);
|
||
newSource.Buffer[newSource.Length/sizeof(WCHAR)] = 0;
|
||
|
||
} else if (RtlPrefixUnicodeString(&DosPrefix, SymbolicLinkName, TRUE)) {
|
||
|
||
newSource.Length = SymbolicLinkName->Length + GlobalPrefix.Length - DosPrefix.Length;
|
||
newSource.MaximumLength = newSource.Length + sizeof(WCHAR);
|
||
newSource.Buffer = ExAllocatePool(PagedPool, newSource.MaximumLength);
|
||
if (!newSource.Buffer) {
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
RtlCopyMemory(newSource.Buffer, GlobalPrefix.Buffer, GlobalPrefix.Length);
|
||
RtlCopyMemory((PCHAR) newSource.Buffer + GlobalPrefix.Length,
|
||
(PCHAR) SymbolicLinkName->Buffer + DosPrefix.Length,
|
||
SymbolicLinkName->Length - DosPrefix.Length);
|
||
newSource.Buffer[newSource.Length/sizeof(WCHAR)] = 0;
|
||
|
||
} else {
|
||
|
||
newSource = *SymbolicLinkName;
|
||
newSource.Buffer = ExAllocatePool(PagedPool, newSource.MaximumLength);
|
||
if (!newSource.Buffer) {
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
RtlCopyMemory(newSource.Buffer, SymbolicLinkName->Buffer,
|
||
SymbolicLinkName->MaximumLength);
|
||
}
|
||
|
||
*StringWithGlobal = newSource;
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
NTSTATUS
|
||
GlobalCreateSymbolicLink(
|
||
IN PUNICODE_STRING SymbolicLinkName,
|
||
IN PUNICODE_STRING DeviceName
|
||
)
|
||
|
||
{
|
||
NTSTATUS status;
|
||
UNICODE_STRING newSource;
|
||
|
||
status = CreateStringWithGlobal(SymbolicLinkName, &newSource);
|
||
if (!NT_SUCCESS(status)) {
|
||
return status;
|
||
}
|
||
|
||
status = IoCreateSymbolicLink(&newSource, DeviceName);
|
||
ExFreePool(newSource.Buffer);
|
||
|
||
return status;
|
||
}
|
||
|
||
NTSTATUS
|
||
GlobalDeleteSymbolicLink(
|
||
IN PUNICODE_STRING SymbolicLinkName
|
||
)
|
||
|
||
{
|
||
NTSTATUS status;
|
||
UNICODE_STRING newSource;
|
||
|
||
status = CreateStringWithGlobal(SymbolicLinkName, &newSource);
|
||
if (!NT_SUCCESS(status)) {
|
||
return status;
|
||
}
|
||
|
||
status = IoDeleteSymbolicLink(&newSource);
|
||
ExFreePool(newSource.Buffer);
|
||
|
||
return status;
|
||
}
|
||
|
||
NTSTATUS
|
||
QueryDeviceInformation(
|
||
IN PUNICODE_STRING NotificationName,
|
||
OUT PUNICODE_STRING DeviceName,
|
||
OUT PMOUNTDEV_UNIQUE_ID* UniqueId,
|
||
OUT PBOOLEAN IsRemovable,
|
||
OUT PBOOLEAN IsRecognized,
|
||
OUT PBOOLEAN IsStable,
|
||
OUT GUID* StableGuid,
|
||
OUT PBOOLEAN IsFT
|
||
)
|
||
|
||
/*++
|
||
|
||
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.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS status, status2;
|
||
PFILE_OBJECT fileObject;
|
||
PDEVICE_OBJECT deviceObject;
|
||
BOOLEAN isRemovable;
|
||
VOLUME_GET_GPT_ATTRIBUTES_INFORMATION gptAttributesInfo;
|
||
PARTITION_INFORMATION_EX partInfo;
|
||
KEVENT event;
|
||
PIRP irp;
|
||
IO_STATUS_BLOCK ioStatus;
|
||
ULONG outputSize;
|
||
PMOUNTDEV_NAME output;
|
||
PIO_STACK_LOCATION irpSp;
|
||
STORAGE_DEVICE_NUMBER number;
|
||
|
||
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) {
|
||
*IsRecognized = TRUE;
|
||
|
||
if (!isRemovable) {
|
||
KeInitializeEvent(&event, NotificationEvent, FALSE);
|
||
irp = IoBuildDeviceIoControlRequest(
|
||
IOCTL_VOLUME_GET_GPT_ATTRIBUTES, deviceObject, NULL, 0,
|
||
&gptAttributesInfo, sizeof(gptAttributesInfo), 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)) {
|
||
if (gptAttributesInfo.GptAttributes&
|
||
GPT_BASIC_DATA_ATTRIBUTE_NO_DRIVE_LETTER) {
|
||
|
||
*IsRecognized = FALSE;
|
||
}
|
||
} else {
|
||
status = STATUS_SUCCESS;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (IsFT) {
|
||
|
||
*IsFT = FALSE;
|
||
|
||
if (!isRemovable) {
|
||
|
||
KeInitializeEvent(&event, NotificationEvent, FALSE);
|
||
irp = IoBuildDeviceIoControlRequest(
|
||
IOCTL_DISK_GET_PARTITION_INFO_EX, 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)) {
|
||
if (partInfo.PartitionStyle == PARTITION_STYLE_MBR) {
|
||
if (IsFT && IsFTPartition(partInfo.Mbr.PartitionType)) {
|
||
*IsFT = TRUE;
|
||
}
|
||
}
|
||
} else {
|
||
status = STATUS_SUCCESS;
|
||
}
|
||
}
|
||
|
||
if (*IsFT) {
|
||
|
||
KeInitializeEvent(&event, NotificationEvent, FALSE);
|
||
irp = IoBuildDeviceIoControlRequest(
|
||
IOCTL_STORAGE_GET_DEVICE_NUMBER, deviceObject, NULL, 0,
|
||
&number, sizeof(number), 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)) {
|
||
*IsFT = FALSE;
|
||
} else {
|
||
status = STATUS_SUCCESS;
|
||
}
|
||
}
|
||
}
|
||
|
||
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)) {
|
||
ExFreePool(output);
|
||
if (DeviceName) {
|
||
ExFreePool(DeviceName->Buffer);
|
||
}
|
||
ObDereferenceObject(deviceObject);
|
||
ObDereferenceObject(fileObject);
|
||
return status;
|
||
}
|
||
|
||
*UniqueId = (PMOUNTDEV_UNIQUE_ID) output;
|
||
}
|
||
|
||
if (IsStable) {
|
||
KeInitializeEvent(&event, NotificationEvent, FALSE);
|
||
irp = IoBuildDeviceIoControlRequest(
|
||
IOCTL_MOUNTDEV_QUERY_STABLE_GUID, deviceObject, NULL, 0,
|
||
StableGuid, sizeof(GUID), FALSE, &event, &ioStatus);
|
||
if (!irp) {
|
||
ObDereferenceObject(deviceObject);
|
||
ObDereferenceObject(fileObject);
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
irpSp = IoGetNextIrpStackLocation(irp);
|
||
irpSp->FileObject = fileObject;
|
||
|
||
status2 = IoCallDriver(deviceObject, irp);
|
||
if (status2 == STATUS_PENDING) {
|
||
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
|
||
status2 = ioStatus.Status;
|
||
}
|
||
|
||
if (NT_SUCCESS(status2)) {
|
||
*IsStable = TRUE;
|
||
} else {
|
||
*IsStable = FALSE;
|
||
}
|
||
}
|
||
|
||
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.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS
|
||
|
||
--*/
|
||
|
||
{
|
||
UNICODE_STRING targetName;
|
||
NTSTATUS status;
|
||
PLIST_ENTRY l;
|
||
PMOUNTED_DEVICE_INFORMATION deviceInfo;
|
||
|
||
if (IsCanonicalName) {
|
||
targetName = *DeviceName;
|
||
} else {
|
||
status = QueryDeviceInformation(DeviceName, &targetName, NULL, NULL,
|
||
NULL, 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.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS
|
||
|
||
--*/
|
||
|
||
{
|
||
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.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS
|
||
|
||
--*/
|
||
|
||
{
|
||
PMOUNTDEV_UNIQUE_ID uniqueId = Context;
|
||
UNICODE_STRING string;
|
||
|
||
if (ValueName[0] == '#' ||
|
||
ValueType != REG_BINARY ||
|
||
uniqueId->UniqueIdLength != ValueLength ||
|
||
RtlCompareMemory(uniqueId->UniqueId, ValueData, ValueLength) !=
|
||
ValueLength) {
|
||
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
RtlInitUnicodeString(&string, ValueName);
|
||
if (!string.Length) {
|
||
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.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS
|
||
|
||
--*/
|
||
|
||
{
|
||
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);
|
||
if (!string.Length) {
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
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
|
||
)
|
||
|
||
{
|
||
if (SymbolicLinkName->Length == 28 &&
|
||
((SymbolicLinkName->Buffer[12] >= 'A' &&
|
||
SymbolicLinkName->Buffer[12] <= 'Z') ||
|
||
SymbolicLinkName->Buffer[12] == 0xFF) &&
|
||
SymbolicLinkName->Buffer[13] == ':') {
|
||
|
||
SymbolicLinkName->Length = 24;
|
||
if (RtlEqualUnicodeString(SymbolicLinkName, &DosDevices, TRUE)) {
|
||
SymbolicLinkName->Length = 28;
|
||
return TRUE;
|
||
}
|
||
SymbolicLinkName->Length = 28;
|
||
}
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
NTSTATUS
|
||
CreateNewVolumeName(
|
||
OUT PUNICODE_STRING VolumeName,
|
||
IN GUID* Guid
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine creates a new name of the form \??\Volume{GUID}.
|
||
|
||
Arguments:
|
||
|
||
VolumeName - Returns the volume name.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS status;
|
||
UUID uuid;
|
||
UNICODE_STRING guidString;
|
||
|
||
if (Guid) {
|
||
uuid = *Guid;
|
||
} else {
|
||
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;
|
||
}
|
||
|
||
RtlCopyUnicodeString(VolumeName, &VolumeNamePrefix);
|
||
RtlAppendUnicodeStringToString(VolumeName, &guidString);
|
||
VolumeName->Buffer[VolumeName->Length/sizeof(WCHAR)] = 0;
|
||
|
||
ExFreePool(guidString.Buffer);
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
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,
|
||
IN BOOLEAN IsStable,
|
||
IN GUID* StableGuid
|
||
)
|
||
|
||
/*++
|
||
|
||
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.
|
||
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS
|
||
|
||
--*/
|
||
|
||
{
|
||
RTL_QUERY_REGISTRY_TABLE queryTable[2];
|
||
BOOLEAN extraLink;
|
||
NTSTATUS status;
|
||
|
||
RtlZeroMemory(queryTable, 2*sizeof(RTL_QUERY_REGISTRY_TABLE));
|
||
queryTable[0].QueryRoutine = SymbolicLinkNamesFromUniqueIdCount;
|
||
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 (IsStable) {
|
||
(*NumNames)++;
|
||
}
|
||
|
||
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].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;
|
||
|
||
if (IsStable) {
|
||
|
||
status = CreateNewVolumeName(&((*SymbolicLinkNames)[0]), StableGuid);
|
||
if (!NT_SUCCESS(status)) {
|
||
ExFreePool(*SymbolicLinkNames);
|
||
return status;
|
||
}
|
||
|
||
queryTable[0].EntryContext = &((*SymbolicLinkNames)[1]);
|
||
} else {
|
||
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.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS
|
||
|
||
--*/
|
||
|
||
{
|
||
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;
|
||
}
|
||
|
||
|
||
VOID
|
||
MigrateRemoteDatabaseWorker (
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PVOID Context
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine migrates an old style remote database to the new format
|
||
(just location change for now) on the given volume.
|
||
|
||
Arguments:
|
||
|
||
OaRemoteDatabase - filled in object attributes for the new remote database volume name.
|
||
OaRemoteDatabaseLegacy - filled in object attributes for the new remote database volume name.
|
||
|
||
|
||
Return Value:
|
||
|
||
A handle to the remote database or NULL.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS status;
|
||
const ULONG copyChunkSize = 512;
|
||
PREMOTE_DATABASE_MIGRATION_CONTEXT migrationContext = Context;
|
||
PMOUNTED_DEVICE_INFORMATION deviceInfo = migrationContext->DeviceInfo;
|
||
BOOLEAN migrationProcessed = FALSE;
|
||
HANDLE remoteDatabase = NULL;
|
||
HANDLE remoteDatabaseLegacy = NULL;
|
||
PVOID buffer;
|
||
OBJECT_ATTRIBUTES oa;
|
||
OBJECT_ATTRIBUTES oaLegacy;
|
||
UNICODE_STRING fileName;
|
||
UNICODE_STRING fileNameLegacy;
|
||
IO_STATUS_BLOCK ioStatus;
|
||
LARGE_INTEGER fileOffset;
|
||
ULONG readBytes;
|
||
FILE_DISPOSITION_INFORMATION DispositionInformation;
|
||
|
||
|
||
|
||
fileName.Length = deviceInfo->DeviceName.Length + RemoteDatabaseFileName.Length;
|
||
fileNameLegacy.Length = deviceInfo->DeviceName.Length + RemoteDatabaseFileNameLegacy.Length;
|
||
|
||
fileName.MaximumLength = fileName.Length + sizeof(WCHAR);
|
||
fileNameLegacy.MaximumLength = fileNameLegacy.Length + sizeof(WCHAR);
|
||
|
||
fileName.Buffer = ExAllocatePoolWithTag (PagedPool, fileName.MaximumLength, MOUNTMGR_TAG_BUFFER);
|
||
fileNameLegacy.Buffer = ExAllocatePoolWithTag (PagedPool, fileNameLegacy.MaximumLength, MOUNTMGR_TAG_BUFFER);
|
||
buffer = ExAllocatePoolWithTag (PagedPool, copyChunkSize, MOUNTMGR_TAG_BUFFER);
|
||
|
||
status = ((NULL == buffer) ||
|
||
(NULL == fileName.Buffer) ||
|
||
(NULL == fileNameLegacy.Buffer))
|
||
? STATUS_INSUFFICIENT_RESOURCES
|
||
: STATUS_SUCCESS;
|
||
|
||
|
||
|
||
if (NT_SUCCESS (status)) {
|
||
|
||
status = RtlCreateSystemVolumeInformationFolder (&deviceInfo->DeviceName);
|
||
}
|
||
|
||
|
||
|
||
if (NT_SUCCESS (status)) {
|
||
|
||
RtlCopyMemory (fileName.Buffer,
|
||
deviceInfo->DeviceName.Buffer,
|
||
deviceInfo->DeviceName.Length);
|
||
|
||
RtlCopyMemory (fileNameLegacy.Buffer,
|
||
deviceInfo->DeviceName.Buffer,
|
||
deviceInfo->DeviceName.Length);
|
||
|
||
|
||
RtlCopyMemory ((PCHAR) fileName.Buffer + deviceInfo->DeviceName.Length,
|
||
RemoteDatabaseFileName.Buffer,
|
||
RemoteDatabaseFileName.Length);
|
||
|
||
RtlCopyMemory ((PCHAR) fileNameLegacy.Buffer + deviceInfo->DeviceName.Length,
|
||
RemoteDatabaseFileNameLegacy.Buffer,
|
||
RemoteDatabaseFileNameLegacy.Length);
|
||
|
||
|
||
fileName.Buffer [fileName.Length / sizeof (WCHAR)] = UNICODE_NULL;
|
||
fileNameLegacy.Buffer [fileNameLegacy.Length / sizeof (WCHAR)] = UNICODE_NULL;
|
||
|
||
|
||
InitializeObjectAttributes (&oa, &fileName, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, 0, 0);
|
||
|
||
status = ZwCreateFile (&remoteDatabase,
|
||
FILE_GENERIC_READ | FILE_GENERIC_WRITE,
|
||
&oa,
|
||
&ioStatus,
|
||
NULL,
|
||
FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM,
|
||
0,
|
||
FILE_CREATE,
|
||
FILE_SYNCHRONOUS_IO_ALERT | FILE_NON_DIRECTORY_FILE,
|
||
NULL,
|
||
0);
|
||
|
||
if (!NT_SUCCESS (status)) {
|
||
remoteDatabase = NULL;
|
||
}
|
||
}
|
||
|
||
|
||
|
||
if (NT_SUCCESS (status)) {
|
||
|
||
InitializeObjectAttributes (&oaLegacy, &fileNameLegacy, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, 0, 0);
|
||
|
||
status = ZwCreateFile (&remoteDatabaseLegacy,
|
||
FILE_GENERIC_READ | FILE_GENERIC_WRITE,
|
||
&oaLegacy,
|
||
&ioStatus,
|
||
NULL,
|
||
FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM,
|
||
0,
|
||
FILE_OPEN,
|
||
FILE_SYNCHRONOUS_IO_ALERT | FILE_NON_DIRECTORY_FILE,
|
||
NULL,
|
||
0);
|
||
|
||
if (!NT_SUCCESS (status)) {
|
||
remoteDatabaseLegacy = NULL;
|
||
}
|
||
|
||
if (STATUS_OBJECT_NAME_NOT_FOUND == status) {
|
||
|
||
/*
|
||
** Failing to open the old database is not considered an
|
||
** error. It just means that there is nothing to migrate
|
||
** so we are done. Other errors are real errors.
|
||
*/
|
||
status = STATUS_SUCCESS;
|
||
migrationProcessed = TRUE;
|
||
}
|
||
}
|
||
|
||
|
||
|
||
|
||
if (NT_SUCCESS (status) && !migrationProcessed) {
|
||
|
||
/*
|
||
** We have a new (empty) database and an old one. If there is
|
||
** stuff in the old one move it over.
|
||
*/
|
||
fileOffset.QuadPart = 0;
|
||
|
||
|
||
while (NT_SUCCESS (status)) {
|
||
status = ZwReadFile (remoteDatabaseLegacy,
|
||
NULL,
|
||
NULL,
|
||
NULL,
|
||
&ioStatus,
|
||
buffer,
|
||
copyChunkSize,
|
||
&fileOffset,
|
||
NULL);
|
||
|
||
if (NT_SUCCESS (status)) {
|
||
|
||
readBytes = (ULONG)ioStatus.Information;
|
||
|
||
status = ZwWriteFile (remoteDatabase,
|
||
NULL,
|
||
NULL,
|
||
NULL,
|
||
&ioStatus,
|
||
buffer,
|
||
readBytes,
|
||
&fileOffset,
|
||
NULL);
|
||
|
||
fileOffset.QuadPart += readBytes;
|
||
}
|
||
}
|
||
|
||
|
||
|
||
if (STATUS_END_OF_FILE == status) {
|
||
status = STATUS_SUCCESS;
|
||
|
||
RtlZeroMemory (&DispositionInformation, sizeof(DispositionInformation));
|
||
|
||
DispositionInformation.DeleteFile = TRUE;
|
||
|
||
status = ZwSetInformationFile (remoteDatabaseLegacy,
|
||
&ioStatus,
|
||
&DispositionInformation,
|
||
sizeof (DispositionInformation),
|
||
FileDispositionInformation);
|
||
|
||
}
|
||
}
|
||
|
||
|
||
|
||
if (NULL != buffer) {
|
||
ExFreePool (buffer);
|
||
}
|
||
|
||
|
||
if (NULL != fileNameLegacy.Buffer) {
|
||
ExFreePool(fileNameLegacy.Buffer);
|
||
}
|
||
|
||
|
||
if (NULL != fileName.Buffer) {
|
||
ExFreePool(fileName.Buffer);
|
||
}
|
||
|
||
|
||
if (NULL != remoteDatabaseLegacy) {
|
||
ZwClose (remoteDatabaseLegacy);
|
||
}
|
||
|
||
|
||
if (NT_SUCCESS (status)) {
|
||
|
||
deviceInfo->RemoteDatabaseMigrated = TRUE;
|
||
|
||
} else if (NULL != remoteDatabase) {
|
||
|
||
ZwClose (remoteDatabase);
|
||
remoteDatabase = NULL;
|
||
}
|
||
|
||
|
||
|
||
IoFreeWorkItem (migrationContext->WorkItem);
|
||
|
||
migrationContext->WorkItem = NULL;
|
||
migrationContext->Status = status;
|
||
migrationContext->Handle = remoteDatabase;
|
||
|
||
KeSetEvent (migrationContext->MigrationProcessedEvent, 0, FALSE);
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
MigrateRemoteDatabase (
|
||
IN PMOUNTED_DEVICE_INFORMATION DeviceInfo,
|
||
OUT PHANDLE RemoteDatabaseHandle
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine invokes MigrateRemoteDatabaseWorker() using a work item
|
||
to migrate the legacy style remote database to the new replacement form.
|
||
|
||
|
||
Arguments:
|
||
|
||
DeviceInfo - device information
|
||
|
||
RemoteDatabaseHandle - returns the handle to the opened remote database
|
||
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS status = STATUS_SUCCESS;
|
||
PREMOTE_DATABASE_MIGRATION_CONTEXT migrationContext = NULL;
|
||
KEVENT migrationProcessed;
|
||
|
||
|
||
KeInitializeEvent (&migrationProcessed, NotificationEvent, FALSE);
|
||
|
||
migrationContext = ExAllocatePool (NonPagedPool,
|
||
sizeof (REMOTE_DATABASE_MIGRATION_CONTEXT));
|
||
|
||
|
||
if (NULL == migrationContext) {
|
||
status = STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
|
||
|
||
if (NT_SUCCESS (status)) {
|
||
RtlZeroMemory (migrationContext, sizeof (*migrationContext));
|
||
|
||
migrationContext->MigrationProcessedEvent = &migrationProcessed;
|
||
migrationContext->DeviceInfo = DeviceInfo;
|
||
migrationContext->WorkItem = IoAllocateWorkItem (DeviceInfo->Extension->DeviceObject);
|
||
|
||
if (NULL == migrationContext->WorkItem) {
|
||
status = STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
}
|
||
|
||
|
||
if (NT_SUCCESS (status)) {
|
||
IoQueueWorkItem (migrationContext->WorkItem,
|
||
MigrateRemoteDatabaseWorker,
|
||
DelayedWorkQueue,
|
||
migrationContext);
|
||
|
||
KeWaitForSingleObject (&migrationProcessed, Executive, KernelMode, FALSE, NULL);
|
||
|
||
status = migrationContext->Status;
|
||
}
|
||
|
||
|
||
*RemoteDatabaseHandle = (NT_SUCCESS (status))
|
||
? migrationContext->Handle
|
||
: NULL;
|
||
|
||
|
||
if (NULL != migrationContext) {
|
||
ExFreePool (migrationContext);
|
||
}
|
||
|
||
|
||
return (status);
|
||
}
|
||
|
||
|
||
HANDLE
|
||
OpenRemoteDatabase(
|
||
IN PMOUNTED_DEVICE_INFORMATION DeviceInfo,
|
||
IN BOOLEAN Create
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine opens the remote database on the given volume.
|
||
|
||
Arguments:
|
||
|
||
DeviceInfo - Supplies the device information.
|
||
|
||
Create - Supplies whether or not to create.
|
||
|
||
Return Value:
|
||
|
||
A handle to the remote database or NULL.
|
||
|
||
--*/
|
||
|
||
{
|
||
UNICODE_STRING fileName;
|
||
OBJECT_ATTRIBUTES oa;
|
||
NTSTATUS status = STATUS_SUCCESS;
|
||
HANDLE remoteDatabase = NULL;
|
||
IO_STATUS_BLOCK ioStatus;
|
||
BOOLEAN attemptFileOpenIf;
|
||
|
||
|
||
fileName.Length = DeviceInfo->DeviceName.Length + RemoteDatabaseFileName.Length;
|
||
fileName.MaximumLength = fileName.Length + sizeof(WCHAR);
|
||
fileName.Buffer = ExAllocatePool (PagedPool, fileName.MaximumLength);
|
||
|
||
if (NULL == fileName.Buffer) {
|
||
status = STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
|
||
|
||
if (NT_SUCCESS (status)) {
|
||
|
||
RtlCopyMemory (fileName.Buffer,
|
||
DeviceInfo->DeviceName.Buffer,
|
||
DeviceInfo->DeviceName.Length);
|
||
|
||
RtlCopyMemory ((PCHAR) fileName.Buffer + DeviceInfo->DeviceName.Length,
|
||
RemoteDatabaseFileName.Buffer,
|
||
RemoteDatabaseFileName.Length);
|
||
|
||
fileName.Buffer [fileName.Length / sizeof (WCHAR)] = UNICODE_NULL;
|
||
|
||
|
||
InitializeObjectAttributes(&oa, &fileName, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, 0, 0);
|
||
|
||
|
||
/*
|
||
** If we have already migrated the database then we can
|
||
** attempt the create here if asked to do so
|
||
*/
|
||
attemptFileOpenIf = Create && DeviceInfo->RemoteDatabaseMigrated;
|
||
|
||
/*
|
||
** Create the remote database file. If we fail to create the
|
||
** file because the SystemVolumeInformation folder is not present,
|
||
** this is the case after formatting the volume, then
|
||
** MigrateRemoteDatabase will run and create the folder.
|
||
*/
|
||
status = ZwCreateFile (&remoteDatabase,
|
||
FILE_GENERIC_READ | FILE_GENERIC_WRITE,
|
||
&oa,
|
||
&ioStatus,
|
||
NULL,
|
||
FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM,
|
||
0,
|
||
attemptFileOpenIf ? FILE_OPEN_IF : FILE_OPEN,
|
||
FILE_SYNCHRONOUS_IO_ALERT | FILE_NON_DIRECTORY_FILE,
|
||
NULL,
|
||
0);
|
||
|
||
|
||
if (Create &&
|
||
!NT_SUCCESS (status) ) {
|
||
|
||
/*
|
||
** This call will attempt to create the SystemVolumeInformation
|
||
** folder, if it doesn't exist, before creating the DB file.
|
||
*/
|
||
status = MigrateRemoteDatabase (DeviceInfo, &remoteDatabase);
|
||
}
|
||
}
|
||
|
||
|
||
|
||
if (NULL != fileName.Buffer) {
|
||
ExFreePool(fileName.Buffer);
|
||
}
|
||
|
||
|
||
return (remoteDatabase);
|
||
}
|
||
|
||
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.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
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.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS
|
||
|
||
--*/
|
||
|
||
{
|
||
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 PMOUNTED_DEVICE_INFORMATION DeviceInfo,
|
||
IN PMOUNTDEV_UNIQUE_ID OldUniqueId,
|
||
IN PMOUNTDEV_UNIQUE_ID NewUniqueId
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine changes the unique id in the remote database.
|
||
|
||
Arguments:
|
||
|
||
DeviceInfo - Supplies the device information.
|
||
|
||
OldUniqueId - Supplies the old unique id.
|
||
|
||
NewUniqueId - Supplies the new unique id.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
HANDLE h;
|
||
ULONG offset, newSize;
|
||
PMOUNTMGR_FILE_ENTRY databaseEntry, newDatabaseEntry;
|
||
NTSTATUS status;
|
||
|
||
h = OpenRemoteDatabase(DeviceInfo, 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.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
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].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, 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.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS status;
|
||
PFILE_OBJECT fileObject;
|
||
PDEVICE_OBJECT deviceObject = NULL;
|
||
ULONG inputSize = sizeof(USHORT) + SymbolicLinkName->Length;
|
||
PMOUNTDEV_NAME input = NULL;
|
||
BOOLEAN objectsReferenced = FALSE;
|
||
KEVENT event;
|
||
PIRP irp;
|
||
IO_STATUS_BLOCK ioStatus;
|
||
PIO_STACK_LOCATION irpSp;
|
||
|
||
|
||
status = IoGetDeviceObjectPointer (SymbolicLinkName,
|
||
FILE_READ_ATTRIBUTES,
|
||
&fileObject,
|
||
&deviceObject);
|
||
|
||
if (NT_SUCCESS (status)) {
|
||
|
||
deviceObject = IoGetAttachedDeviceReference (fileObject->DeviceObject);
|
||
|
||
objectsReferenced = TRUE;
|
||
|
||
|
||
input = ExAllocatePool (PagedPool, inputSize);
|
||
|
||
if (!input) {
|
||
status = STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
}
|
||
|
||
|
||
|
||
if (NT_SUCCESS (status)) {
|
||
input->NameLength = SymbolicLinkName->Length;
|
||
|
||
RtlCopyMemory (input->Name,
|
||
SymbolicLinkName->Buffer,
|
||
SymbolicLinkName->Length);
|
||
|
||
|
||
|
||
/*
|
||
** First send the notification using the standard IOCTL. When
|
||
** that's done we'll send another using the obsolete IOCTL for
|
||
** all those third parties who have yet to recompile.
|
||
*/
|
||
KeInitializeEvent(&event, NotificationEvent, FALSE);
|
||
|
||
irp = IoBuildDeviceIoControlRequest (IOCTL_MOUNTDEV_LINK_CREATED,
|
||
deviceObject,
|
||
input,
|
||
inputSize,
|
||
NULL,
|
||
0,
|
||
FALSE,
|
||
&event,
|
||
&ioStatus);
|
||
|
||
if (NULL != irp) {
|
||
|
||
irpSp = IoGetNextIrpStackLocation (irp);
|
||
irpSp->FileObject = fileObject;
|
||
|
||
status = IoCallDriver (deviceObject, irp);
|
||
|
||
if (status == STATUS_PENDING) {
|
||
KeWaitForSingleObject (&event, Executive, KernelMode, FALSE, NULL);
|
||
}
|
||
}
|
||
|
||
|
||
|
||
/*
|
||
** Now the 'obsolete' non-protected IOCTL
|
||
*/
|
||
KeInitializeEvent(&event, NotificationEvent, FALSE);
|
||
|
||
irp = IoBuildDeviceIoControlRequest (IOCTL_MOUNTDEV_LINK_CREATED_OBSOLETE,
|
||
deviceObject,
|
||
input,
|
||
inputSize,
|
||
NULL,
|
||
0,
|
||
FALSE,
|
||
&event,
|
||
&ioStatus);
|
||
|
||
if (NULL != irp) {
|
||
|
||
irpSp = IoGetNextIrpStackLocation (irp);
|
||
irpSp->FileObject = fileObject;
|
||
|
||
status = IoCallDriver (deviceObject, irp);
|
||
|
||
if (status == STATUS_PENDING) {
|
||
KeWaitForSingleObject (&event, Executive, KernelMode, FALSE, NULL);
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
|
||
if (NULL != input) {
|
||
ExFreePool (input);
|
||
}
|
||
|
||
if (objectsReferenced) {
|
||
ObDereferenceObject (deviceObject);
|
||
ObDereferenceObject (fileObject);
|
||
}
|
||
|
||
|
||
return;
|
||
}
|
||
|
||
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.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
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;
|
||
UCHAR driveLetter;
|
||
|
||
DriveLetterName->MaximumLength = 30;
|
||
DriveLetterName->Buffer = ExAllocatePool(PagedPool,
|
||
DriveLetterName->MaximumLength);
|
||
if (!DriveLetterName->Buffer) {
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
RtlCopyUnicodeString(DriveLetterName, &DosDevices);
|
||
|
||
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 = GlobalCreateSymbolicLink(DriveLetterName, TargetName);
|
||
if (NT_SUCCESS(status)) {
|
||
return status;
|
||
}
|
||
}
|
||
|
||
if (RtlPrefixUnicodeString(&DeviceFloppy, TargetName, TRUE)) {
|
||
driveLetter = 'A';
|
||
} else if (RtlPrefixUnicodeString(&DeviceCdRom, TargetName, TRUE)) {
|
||
driveLetter = 'D';
|
||
} else {
|
||
driveLetter = 'C';
|
||
}
|
||
|
||
for (; driveLetter <= 'Z'; driveLetter++) {
|
||
DriveLetterName->Buffer[12] = driveLetter;
|
||
|
||
status = GlobalCreateSymbolicLink(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].EntryContext = &hasNoDriveLetterEntry;
|
||
|
||
hasNoDriveLetterEntry = FALSE;
|
||
RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE, MOUNTED_DEVICES_KEY,
|
||
queryTable, UniqueId, NULL);
|
||
|
||
return hasNoDriveLetterEntry;
|
||
}
|
||
|
||
typedef struct _CHANGE_NOTIFY_WORK_ITEM {
|
||
LIST_ENTRY List; // Chained to the extension via this for unload
|
||
PIO_WORKITEM WorkItem;
|
||
PDEVICE_EXTENSION Extension;
|
||
PIRP Irp;
|
||
PVOID SystemBuffer;
|
||
PFILE_OBJECT FileObject;
|
||
PKEVENT Event;
|
||
UNICODE_STRING DeviceName;
|
||
ULONG OutputSize;
|
||
CCHAR StackSize;
|
||
} CHANGE_NOTIFY_WORK_ITEM, *PCHANGE_NOTIFY_WORK_ITEM;
|
||
|
||
VOID
|
||
RemoveWorkItem(
|
||
IN PCHANGE_NOTIFY_WORK_ITEM WorkItem
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine removes a work item if its still chained to the device extension and frees it if needed or
|
||
wakes up the waiter if unload is trying to cancel these operations.
|
||
|
||
Arguments:
|
||
|
||
WorkItem - Supplies the work item.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
{
|
||
KeWaitForSingleObject(&WorkItem->Extension->Mutex,
|
||
Executive,
|
||
KernelMode,
|
||
FALSE,
|
||
NULL);
|
||
|
||
if (WorkItem->Event == NULL) {
|
||
RemoveEntryList (&WorkItem->List);
|
||
|
||
KeReleaseSemaphore(&WorkItem->Extension->Mutex, IO_NO_INCREMENT, 1, FALSE);
|
||
|
||
IoFreeIrp(WorkItem->Irp);
|
||
ExFreePool(WorkItem->DeviceName.Buffer);
|
||
ExFreePool(WorkItem->SystemBuffer);
|
||
ExFreePool(WorkItem);
|
||
} else {
|
||
KeReleaseSemaphore(&WorkItem->Extension->Mutex, IO_NO_INCREMENT, 1, FALSE);
|
||
|
||
KeSetEvent(WorkItem->Event, 0, FALSE);
|
||
}
|
||
}
|
||
|
||
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.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
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)) {
|
||
RemoveWorkItem (WorkItem);
|
||
return;
|
||
}
|
||
deviceObject = IoGetAttachedDeviceReference(fileObject->DeviceObject);
|
||
WorkItem->FileObject = fileObject;
|
||
|
||
irp = WorkItem->Irp;
|
||
IoInitializeIrp(irp, IoSizeOfIrp(WorkItem->StackSize),
|
||
WorkItem->StackSize);
|
||
|
||
//
|
||
// IoCancelIrp could have been called by the unload code and the cancel flag over written by the call
|
||
// above. To handle this case we check the work item to see if the event address has been set up.
|
||
// We do this with an interlocked sequence (that does nothing) to make sure the ordering is preserved.
|
||
// We don't want the read of the pointer field to be earlier than the IRP initializing code above.
|
||
//
|
||
if (InterlockedCompareExchangePointer (&WorkItem->Event, NULL, NULL) != NULL) {
|
||
ObDereferenceObject(fileObject);
|
||
ObDereferenceObject(deviceObject);
|
||
RemoveWorkItem (WorkItem);
|
||
return;
|
||
}
|
||
|
||
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;
|
||
|
||
status = IoSetCompletionRoutineEx (WorkItem->Extension->DeviceObject,
|
||
irp,
|
||
UniqueIdChangeNotifyCompletion,
|
||
WorkItem,
|
||
TRUE,
|
||
TRUE,
|
||
TRUE);
|
||
if (!NT_SUCCESS (status)) {
|
||
ObDereferenceObject(fileObject);
|
||
ObDereferenceObject(deviceObject);
|
||
RemoveWorkItem (WorkItem);
|
||
return;
|
||
}
|
||
|
||
IoCallDriver(deviceObject, irp);
|
||
|
||
ObDereferenceObject(fileObject);
|
||
ObDereferenceObject(deviceObject);
|
||
}
|
||
|
||
VOID
|
||
UniqueIdChangeNotifyWorker(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PVOID WorkItem
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine updates the unique id in the database with the new version.
|
||
|
||
Arguments:
|
||
|
||
DeviceObject - Device object
|
||
WorkItem - Supplies the work item.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PCHANGE_NOTIFY_WORK_ITEM workItem = WorkItem;
|
||
PMOUNTDEV_UNIQUE_ID_CHANGE_NOTIFY_OUTPUT output;
|
||
PMOUNTDEV_UNIQUE_ID oldUniqueId, newUniqueId;
|
||
|
||
if (!NT_SUCCESS(workItem->Irp->IoStatus.Status)) {
|
||
RemoveWorkItem (WorkItem);
|
||
return;
|
||
}
|
||
|
||
output = workItem->Irp->AssociatedIrp.SystemBuffer;
|
||
|
||
oldUniqueId = ExAllocatePool(PagedPool, sizeof(MOUNTDEV_UNIQUE_ID) +
|
||
output->OldUniqueIdLength);
|
||
if (!oldUniqueId) {
|
||
RemoveWorkItem (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);
|
||
RemoveWorkItem (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.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
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->Event = NULL;
|
||
workItem->WorkItem = IoAllocateWorkItem (Extension->DeviceObject);
|
||
if (workItem->WorkItem == NULL) {
|
||
ObDereferenceObject(deviceObject);
|
||
ExFreePool(workItem);
|
||
return;
|
||
}
|
||
|
||
workItem->Extension = Extension;
|
||
workItem->StackSize = deviceObject->StackSize;
|
||
workItem->Irp = IoAllocateIrp(deviceObject->StackSize, FALSE);
|
||
ObDereferenceObject(deviceObject);
|
||
if (!workItem->Irp) {
|
||
IoFreeWorkItem (workItem->WorkItem);
|
||
ExFreePool(workItem);
|
||
return;
|
||
}
|
||
|
||
outputSize = sizeof(MOUNTDEV_UNIQUE_ID_CHANGE_NOTIFY_OUTPUT) + 1024;
|
||
output = ExAllocatePool(NonPagedPool, outputSize);
|
||
if (!output) {
|
||
IoFreeIrp(workItem->Irp);
|
||
IoFreeWorkItem (workItem->WorkItem);
|
||
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);
|
||
IoFreeWorkItem (workItem->WorkItem);
|
||
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;
|
||
|
||
KeWaitForSingleObject(&Extension->Mutex,
|
||
Executive,
|
||
KernelMode,
|
||
FALSE,
|
||
NULL);
|
||
|
||
InsertTailList (&Extension->UniqueIdChangeNotifyList, &workItem->List);
|
||
|
||
KeReleaseSemaphore(&Extension->Mutex, IO_NO_INCREMENT, 1, FALSE);
|
||
|
||
IssueUniqueIdChangeNotifyWorker(workItem, UniqueId);
|
||
}
|
||
|
||
NTSTATUS
|
||
QueryVolumeName(
|
||
IN HANDLE Handle,
|
||
IN PLONGLONG FileReference,
|
||
IN PUNICODE_STRING DirectoryName,
|
||
IN OUT PUNICODE_STRING VolumeName,
|
||
OUT PUNICODE_STRING PathName
|
||
)
|
||
|
||
/*++
|
||
|
||
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.
|
||
|
||
--*/
|
||
|
||
{
|
||
OBJECT_ATTRIBUTES oa;
|
||
NTSTATUS status;
|
||
HANDLE h;
|
||
IO_STATUS_BLOCK ioStatus;
|
||
UNICODE_STRING fileId;
|
||
PREPARSE_DATA_BUFFER reparse;
|
||
ULONG nameInfoSize;
|
||
PFILE_NAME_INFORMATION nameInfo;
|
||
|
||
if (DirectoryName) {
|
||
|
||
InitializeObjectAttributes(&oa, DirectoryName, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, 0,
|
||
0);
|
||
|
||
status = ZwOpenFile(
|
||
&h,
|
||
FILE_READ_ATTRIBUTES | SYNCHRONIZE,
|
||
&oa,
|
||
&ioStatus,
|
||
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
||
FILE_OPEN_REPARSE_POINT | FILE_SYNCHRONOUS_IO_NONALERT
|
||
);
|
||
|
||
} else {
|
||
fileId.Length = sizeof(LONGLONG);
|
||
fileId.MaximumLength = fileId.Length;
|
||
fileId.Buffer = (PWSTR) FileReference;
|
||
|
||
InitializeObjectAttributes(&oa, &fileId, OBJ_KERNEL_HANDLE, Handle, NULL);
|
||
|
||
status = ZwOpenFile(
|
||
&h,
|
||
FILE_READ_ATTRIBUTES | SYNCHRONIZE,
|
||
&oa,
|
||
&ioStatus,
|
||
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
||
FILE_OPEN_BY_FILE_ID | FILE_OPEN_REPARSE_POINT
|
||
| FILE_SYNCHRONOUS_IO_NONALERT
|
||
);
|
||
}
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
return status;
|
||
}
|
||
|
||
reparse = ExAllocatePool(PagedPool, MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
|
||
if (!reparse) {
|
||
ZwClose(h);
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
status = ZwFsControlFile(h, NULL, NULL, NULL, &ioStatus,
|
||
FSCTL_GET_REPARSE_POINT, NULL, 0, reparse,
|
||
MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
|
||
if (!NT_SUCCESS(status)) {
|
||
ExFreePool(reparse);
|
||
ZwClose(h);
|
||
return status;
|
||
}
|
||
|
||
if (reparse->MountPointReparseBuffer.SubstituteNameLength + sizeof(WCHAR) >
|
||
VolumeName->MaximumLength) {
|
||
|
||
ExFreePool(reparse);
|
||
ZwClose(h);
|
||
return STATUS_BUFFER_TOO_SMALL;
|
||
}
|
||
|
||
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] != '\\') {
|
||
ZwClose(h);
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
VolumeName->Length -= sizeof(WCHAR);
|
||
VolumeName->Buffer[VolumeName->Length/sizeof(WCHAR)] = 0;
|
||
|
||
if (!MOUNTMGR_IS_NT_VOLUME_NAME(VolumeName)) {
|
||
ZwClose(h);
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
nameInfoSize = sizeof(FILE_NAME_INFORMATION);
|
||
nameInfo = ExAllocatePool(PagedPool, nameInfoSize);
|
||
if (!nameInfo) {
|
||
ZwClose(h);
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
status = ZwQueryInformationFile(h, &ioStatus, nameInfo, nameInfoSize,
|
||
FileNameInformation);
|
||
if (status == STATUS_BUFFER_OVERFLOW) {
|
||
nameInfoSize = sizeof(FILE_NAME_INFORMATION) +
|
||
nameInfo->FileNameLength;
|
||
ExFreePool(nameInfo);
|
||
nameInfo = ExAllocatePool(PagedPool, nameInfoSize);
|
||
if (!nameInfo) {
|
||
ZwClose(h);
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
status = ZwQueryInformationFile(h, &ioStatus, nameInfo, nameInfoSize,
|
||
FileNameInformation);
|
||
}
|
||
|
||
ZwClose(h);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
ExFreePool(nameInfo);
|
||
return status;
|
||
}
|
||
|
||
PathName->Length = (USHORT) nameInfo->FileNameLength;
|
||
PathName->MaximumLength = PathName->Length + sizeof(WCHAR);
|
||
PathName->Buffer = ExAllocatePool(PagedPool, PathName->MaximumLength);
|
||
if (!PathName->Buffer) {
|
||
ExFreePool(nameInfo);
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
RtlCopyMemory(PathName->Buffer, nameInfo->FileName, PathName->Length);
|
||
PathName->Buffer[PathName->Length/sizeof(WCHAR)] = 0;
|
||
|
||
ExFreePool(nameInfo);
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
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 PDEVICE_EXTENSION Extension,
|
||
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];
|
||
NTSTATUS status;
|
||
PMOUNTED_DEVICE_INFORMATION deviceInfo;
|
||
|
||
RtlZeroMemory(queryTable, 2*sizeof(RTL_QUERY_REGISTRY_TABLE));
|
||
queryTable[0].QueryRoutine = QueryUniqueIdQueryRoutine;
|
||
queryTable[0].Name = VolumeName->Buffer;
|
||
|
||
*UniqueId = NULL;
|
||
RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE, MOUNTED_DEVICES_KEY,
|
||
queryTable, UniqueId, NULL);
|
||
|
||
if (!(*UniqueId)) {
|
||
status = FindDeviceInfo(Extension, VolumeName, FALSE, &deviceInfo);
|
||
if (!NT_SUCCESS(status)) {
|
||
return status;
|
||
}
|
||
|
||
*UniqueId = ExAllocatePool(PagedPool, sizeof(MOUNTDEV_UNIQUE_ID) +
|
||
deviceInfo->UniqueId->UniqueIdLength);
|
||
if (!*UniqueId) {
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
(*UniqueId)->UniqueIdLength = deviceInfo->UniqueId->UniqueIdLength;
|
||
RtlCopyMemory((*UniqueId)->UniqueId, deviceInfo->UniqueId->UniqueId,
|
||
deviceInfo->UniqueId->UniqueIdLength);
|
||
}
|
||
|
||
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;
|
||
|
||
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.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
RTL_QUERY_REGISTRY_TABLE queryTable[2];
|
||
|
||
RtlZeroMemory(queryTable, 2*sizeof(RTL_QUERY_REGISTRY_TABLE));
|
||
queryTable[0].QueryRoutine = DeleteNoDriveLetterEntryRoutine;
|
||
|
||
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.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
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);
|
||
}
|
||
|
||
VOID
|
||
SendOnlineNotificationWorker(
|
||
IN PVOID Context
|
||
)
|
||
|
||
{
|
||
PMOUNTMGR_ONLINE_CONTEXT context = Context;
|
||
|
||
SendOnlineNotification(&context->NotificationName);
|
||
ExFreePool(context->NotificationName.Buffer);
|
||
ExFreePool(context);
|
||
}
|
||
|
||
VOID
|
||
PostOnlineNotification(
|
||
IN PUNICODE_STRING NotificationName
|
||
)
|
||
|
||
{
|
||
PMOUNTMGR_ONLINE_CONTEXT context;
|
||
|
||
context = ExAllocatePool(NonPagedPool, sizeof(MOUNTMGR_ONLINE_CONTEXT));
|
||
if (!context) {
|
||
return;
|
||
}
|
||
|
||
ExInitializeWorkItem(&context->WorkItem, SendOnlineNotificationWorker,
|
||
context);
|
||
context->NotificationName.Length = NotificationName->Length;
|
||
context->NotificationName.MaximumLength =
|
||
context->NotificationName.Length + sizeof(WCHAR);
|
||
context->NotificationName.Buffer = ExAllocatePool(NonPagedPool,
|
||
context->NotificationName.MaximumLength);
|
||
if (!context->NotificationName.Buffer) {
|
||
ExFreePool(context);
|
||
return;
|
||
}
|
||
|
||
RtlCopyMemory(context->NotificationName.Buffer, NotificationName->Buffer,
|
||
context->NotificationName.Length);
|
||
context->NotificationName.Buffer[
|
||
context->NotificationName.Length/sizeof(WCHAR)] = 0;
|
||
|
||
ExQueueWorkItem(&context->WorkItem, DelayedWorkQueue);
|
||
}
|
||
|
||
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, d;
|
||
PSYMBOLIC_LINK_NAME_ENTRY symlinkEntry;
|
||
|
||
symbolicLinkName = *SymbolicLinkName;
|
||
deviceName = *DeviceName;
|
||
|
||
status = QueryDeviceInformation(&deviceName, &targetName, NULL, NULL,
|
||
NULL, 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, NULL, NULL, NULL);
|
||
if (!NT_SUCCESS(status)) {
|
||
ExFreePool(symName);
|
||
ExFreePool(targetName.Buffer);
|
||
return status;
|
||
}
|
||
|
||
status = GlobalCreateSymbolicLink(&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 = GlobalCreateSymbolicLink(&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)) {
|
||
GlobalDeleteSymbolicLink(&symbolicLinkName);
|
||
ExFreePool(symName);
|
||
return status;
|
||
}
|
||
|
||
symlinkEntry = ExAllocatePool(PagedPool, sizeof(SYMBOLIC_LINK_NAME_ENTRY));
|
||
if (!symlinkEntry) {
|
||
GlobalDeleteSymbolicLink(&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);
|
||
GlobalDeleteSymbolicLink(&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);
|
||
if (!deviceInfo->InOfflineList) {
|
||
PostOnlineNotification(&deviceInfo->NotificationName);
|
||
}
|
||
}
|
||
|
||
if (MOUNTMGR_IS_NT_VOLUME_NAME(&symbolicLinkName) &&
|
||
Extension->AutomaticDriveLetterAssignment) {
|
||
|
||
for (l = Extension->MountedDeviceList.Flink;
|
||
l != &Extension->MountedDeviceList; l = l->Flink) {
|
||
|
||
d = CONTAINING_RECORD(l, MOUNTED_DEVICE_INFORMATION, ListEntry);
|
||
if (d->HasDanglingVolumeMountPoint) {
|
||
ReconcileThisDatabaseWithMaster(Extension, d);
|
||
}
|
||
}
|
||
}
|
||
|
||
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.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
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.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PRECONCILE_WORK_ITEM_INFO workItem = WorkItem;
|
||
PDEVICE_EXTENSION Extension;
|
||
PMOUNTED_DEVICE_INFORMATION DeviceInfo;
|
||
PLIST_ENTRY l, ll, s;
|
||
PMOUNTED_DEVICE_INFORMATION deviceInfo;
|
||
HANDLE remoteDatabaseHandle, indexHandle, junctionHandle;
|
||
UNICODE_STRING indexName, pathName;
|
||
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;
|
||
REPARSE_INDEX_KEY reparseKey;
|
||
UNICODE_STRING reparseName;
|
||
PMOUNTMGR_MOUNT_POINT_ENTRY mountPointEntry;
|
||
BOOLEAN actualDanglesFound;
|
||
|
||
Extension = workItem->Extension;
|
||
DeviceInfo = workItem->DeviceInfo;
|
||
|
||
if (Unloading) {
|
||
return;
|
||
}
|
||
|
||
status = WaitForRemoteDatabaseSemaphore(Extension);
|
||
if (!NT_SUCCESS(status)) {
|
||
ASSERT(FALSE);
|
||
return;
|
||
}
|
||
|
||
if (Unloading) {
|
||
ReleaseRemoteDatabaseSemaphore(Extension);
|
||
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;
|
||
}
|
||
|
||
DeviceInfo->ReconcileOnMounts = TRUE;
|
||
DeviceInfo->HasDanglingVolumeMountPoint = TRUE;
|
||
actualDanglesFound = FALSE;
|
||
|
||
for (l = Extension->MountedDeviceList.Flink;
|
||
l != &Extension->MountedDeviceList; l = l->Flink) {
|
||
|
||
deviceInfo = CONTAINING_RECORD(l, MOUNTED_DEVICE_INFORMATION,
|
||
ListEntry);
|
||
|
||
for (ll = deviceInfo->MountPointsPointingHere.Flink;
|
||
ll != &deviceInfo->MountPointsPointingHere; ll = ll->Flink) {
|
||
|
||
mountPointEntry = CONTAINING_RECORD(ll, MOUNTMGR_MOUNT_POINT_ENTRY,
|
||
ListEntry);
|
||
if (mountPointEntry->DeviceInfo == DeviceInfo) {
|
||
s = ll->Blink;
|
||
RemoveEntryList(ll);
|
||
ExFreePool(mountPointEntry->MountPath.Buffer);
|
||
ExFreePool(mountPointEntry);
|
||
ll = s;
|
||
}
|
||
}
|
||
}
|
||
|
||
remoteDatabaseHandle = OpenRemoteDatabase(DeviceInfo, FALSE);
|
||
|
||
indexName.Length = DeviceInfo->DeviceName.Length +
|
||
ReparseIndexName.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,
|
||
ReparseIndexName.Buffer, ReparseIndexName.Length);
|
||
indexName.Buffer[indexName.Length/sizeof(WCHAR)] = 0;
|
||
|
||
InitializeObjectAttributes(&oa, &indexName, OBJ_KERNEL_HANDLE | 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);
|
||
}
|
||
DeviceInfo->HasDanglingVolumeMountPoint = FALSE;
|
||
KeReleaseSemaphore(&Extension->Mutex, IO_NO_INCREMENT, 1, FALSE);
|
||
ReleaseRemoteDatabaseSemaphore(Extension);
|
||
return;
|
||
}
|
||
|
||
RtlZeroMemory(&reparseKey, sizeof(reparseKey));
|
||
reparseKey.FileReparseTag = IO_REPARSE_TAG_MOUNT_POINT;
|
||
reparseName.Length = reparseName.MaximumLength = sizeof(reparseKey);
|
||
reparseName.Buffer = (PWCHAR) &reparseKey;
|
||
status = ZwQueryDirectoryFile(indexHandle, NULL, NULL, NULL, &ioStatus,
|
||
&reparseInfo, sizeof(reparseInfo),
|
||
FileReparsePointInformation, TRUE,
|
||
&reparseName, 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, 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,
|
||
restartScan ? &reparseName : NULL,
|
||
restartScan);
|
||
if (restartScan) {
|
||
restartScan = FALSE;
|
||
} else {
|
||
if (previousReparseInfo.FileReference ==
|
||
reparseInfo.FileReference &&
|
||
previousReparseInfo.Tag == reparseInfo.Tag) {
|
||
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (!NT_SUCCESS(status) || Unloading) {
|
||
break;
|
||
}
|
||
|
||
if (reparseInfo.Tag != IO_REPARSE_TAG_MOUNT_POINT) {
|
||
break;
|
||
}
|
||
|
||
status = QueryVolumeName(indexHandle, &reparseInfo.FileReference, NULL,
|
||
&volumeName, &pathName);
|
||
if (!NT_SUCCESS(status)) {
|
||
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(Extension, &volumeName, &uniqueId);
|
||
KeReleaseSemaphore(&Extension->Mutex, IO_NO_INCREMENT, 1, FALSE);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
goto BuildMountPointGraph;
|
||
}
|
||
|
||
entryLength = sizeof(MOUNTMGR_FILE_ENTRY) +
|
||
volumeName.Length + uniqueId->UniqueIdLength;
|
||
entry = ExAllocatePool(PagedPool, entryLength);
|
||
if (!entry) {
|
||
ExFreePool(uniqueId);
|
||
goto BuildMountPointGraph;
|
||
}
|
||
|
||
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)) {
|
||
ExFreePool(pathName.Buffer);
|
||
ZwClose(indexHandle);
|
||
CloseRemoteDatabase(remoteDatabaseHandle);
|
||
ReleaseRemoteDatabaseSemaphore(Extension);
|
||
return;
|
||
}
|
||
|
||
goto BuildMountPointGraph;
|
||
}
|
||
|
||
if (entry->RefCount) {
|
||
|
||
entry->RefCount++;
|
||
status = WriteRemoteDatabaseEntry(remoteDatabaseHandle, offset,
|
||
entry);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
ExFreePool(entry);
|
||
ExFreePool(pathName.Buffer);
|
||
ZwClose(indexHandle);
|
||
CloseRemoteDatabase(remoteDatabaseHandle);
|
||
ReleaseRemoteDatabaseSemaphore(Extension);
|
||
return;
|
||
}
|
||
|
||
} else {
|
||
|
||
KeWaitForSingleObject(&Extension->Mutex, Executive, KernelMode,
|
||
FALSE, NULL);
|
||
|
||
status = QueryUniqueIdFromMaster(Extension, &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);
|
||
ExFreePool(pathName.Buffer);
|
||
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);
|
||
ExFreePool(pathName.Buffer);
|
||
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);
|
||
ExFreePool(pathName.Buffer);
|
||
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);
|
||
ExFreePool(pathName.Buffer);
|
||
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);
|
||
ExFreePool(pathName.Buffer);
|
||
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);
|
||
ExFreePool(pathName.Buffer);
|
||
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);
|
||
ExFreePool(pathName.Buffer);
|
||
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);
|
||
ExFreePool(pathName.Buffer);
|
||
ZwClose(indexHandle);
|
||
CloseRemoteDatabase(remoteDatabaseHandle);
|
||
ReleaseRemoteDatabaseSemaphore(Extension);
|
||
return;
|
||
}
|
||
}
|
||
|
||
KeReleaseSemaphore(&Extension->Mutex, IO_NO_INCREMENT, 1, FALSE);
|
||
}
|
||
|
||
ExFreePool(entry);
|
||
|
||
BuildMountPointGraph:
|
||
|
||
KeWaitForSingleObject(&Extension->Mutex, Executive, KernelMode,
|
||
FALSE, NULL);
|
||
|
||
status = FindDeviceInfo(Extension, &volumeName, FALSE, &deviceInfo);
|
||
if (NT_SUCCESS(status)) {
|
||
|
||
mountPointEntry = (PMOUNTMGR_MOUNT_POINT_ENTRY)
|
||
ExAllocatePool(PagedPool,
|
||
sizeof(MOUNTMGR_MOUNT_POINT_ENTRY));
|
||
if (mountPointEntry) {
|
||
InsertTailList(&deviceInfo->MountPointsPointingHere,
|
||
&mountPointEntry->ListEntry);
|
||
mountPointEntry->DeviceInfo = DeviceInfo;
|
||
mountPointEntry->MountPath = pathName;
|
||
} else {
|
||
ExFreePool(pathName.Buffer);
|
||
}
|
||
|
||
if (!deviceInfo->InOfflineList) {
|
||
PostOnlineNotification(&deviceInfo->NotificationName);
|
||
}
|
||
} else {
|
||
actualDanglesFound = TRUE;
|
||
ExFreePool(pathName.Buffer);
|
||
}
|
||
|
||
KeReleaseSemaphore(&Extension->Mutex, IO_NO_INCREMENT, 1, FALSE);
|
||
}
|
||
|
||
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);
|
||
}
|
||
|
||
if (deviceInfo && !actualDanglesFound) {
|
||
DeviceInfo->HasDanglingVolumeMountPoint = FALSE;
|
||
}
|
||
|
||
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.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PRECONCILE_WORK_ITEM workItem;
|
||
|
||
if (DeviceInfo->IsRemovable) {
|
||
return;
|
||
}
|
||
|
||
workItem = ExAllocatePool(NonPagedPool,
|
||
sizeof(RECONCILE_WORK_ITEM));
|
||
if (!workItem) {
|
||
return;
|
||
}
|
||
|
||
workItem->WorkItem = IoAllocateWorkItem(Extension->DeviceObject);
|
||
if (workItem->WorkItem == NULL) {
|
||
ExFreePool (workItem);
|
||
return;
|
||
}
|
||
|
||
workItem->WorkerRoutine = ReconcileThisDatabaseWithMasterWorker;
|
||
workItem->WorkItemInfo.Extension = Extension;
|
||
workItem->WorkItemInfo.DeviceInfo = DeviceInfo;
|
||
|
||
QueueWorkItem(Extension, workItem, &workItem->WorkItemInfo);
|
||
}
|
||
|
||
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.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
RTL_QUERY_REGISTRY_TABLE queryTable[2];
|
||
|
||
RtlZeroMemory(queryTable, 2*sizeof(RTL_QUERY_REGISTRY_TABLE));
|
||
queryTable[0].QueryRoutine = DeleteFromLocalDatabaseRoutine;
|
||
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.
|
||
|
||
Arguments:
|
||
|
||
|
||
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.
|
||
|
||
GlobalDeleteSymbolicLink(SymbolicLinkName);
|
||
GlobalCreateSymbolicLink(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.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
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.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
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)) {
|
||
|
||
MountMgrMountedDeviceRemoval(extension, &deviceInfo->NotificationName);
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
if (IsEqualGUID(¬ification->Event, &GUID_IO_VOLUME_MOUNT) &&
|
||
deviceInfo->ReconcileOnMounts) {
|
||
|
||
deviceInfo->ReconcileOnMounts = FALSE;
|
||
ReconcileThisDatabaseWithMaster(extension, deviceInfo);
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
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.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
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);
|
||
}
|
||
|
||
VOID
|
||
MountMgrFreeDeadDeviceInfo(
|
||
PMOUNTED_DEVICE_INFORMATION deviceInfo
|
||
)
|
||
{
|
||
ExFreePool(deviceInfo->NotificationName.Buffer);
|
||
ExFreePool(deviceInfo);
|
||
}
|
||
|
||
VOID
|
||
MountMgrFreeSavedLink(
|
||
PSAVED_LINKS_INFORMATION savedLinks
|
||
)
|
||
{
|
||
PLIST_ENTRY l;
|
||
PSYMBOLIC_LINK_NAME_ENTRY symlinkEntry;
|
||
|
||
while (!IsListEmpty(&savedLinks->SymbolicLinkNames)) {
|
||
l = RemoveHeadList(&savedLinks->SymbolicLinkNames);
|
||
symlinkEntry = CONTAINING_RECORD(l,
|
||
SYMBOLIC_LINK_NAME_ENTRY,
|
||
ListEntry);
|
||
GlobalDeleteSymbolicLink(&symlinkEntry->SymbolicLinkName);
|
||
ExFreePool(symlinkEntry->SymbolicLinkName.Buffer);
|
||
ExFreePool(symlinkEntry);
|
||
}
|
||
ExFreePool(savedLinks->UniqueId);
|
||
ExFreePool(savedLinks);
|
||
}
|
||
|
||
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, uniqueIdCopy;
|
||
BOOLEAN isRecognized, sendOnline;
|
||
UNICODE_STRING suggestedName;
|
||
BOOLEAN useOnlyIfThereAreNoOtherLinks;
|
||
PUNICODE_STRING symbolicLinkNames;
|
||
ULONG numNames, i, allocSize;
|
||
BOOLEAN hasDriveLetter, offline, isStable, isFT;
|
||
BOOLEAN hasVolumeName, isLinkPreset;
|
||
PSYMBOLIC_LINK_NAME_ENTRY symlinkEntry;
|
||
UNICODE_STRING volumeName;
|
||
UNICODE_STRING driveLetterName;
|
||
PSAVED_LINKS_INFORMATION savedLinks;
|
||
PLIST_ENTRY l;
|
||
GUID stableGuid;
|
||
|
||
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);
|
||
InitializeListHead(&deviceInfo->MountPointsPointingHere);
|
||
|
||
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,
|
||
&isStable, &stableGuid, &isFT);
|
||
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 {
|
||
MountMgrFreeDeadDeviceInfo (deviceInfo);
|
||
}
|
||
|
||
KeReleaseSemaphore(&extension->Mutex, IO_NO_INCREMENT, 1, FALSE);
|
||
|
||
return status;
|
||
}
|
||
|
||
deviceInfo->UniqueId = uniqueId;
|
||
deviceInfo->DeviceName = targetName;
|
||
deviceInfo->KeepLinksWhenOffline = FALSE;
|
||
|
||
if (extension->SystemPartitionUniqueId &&
|
||
uniqueId->UniqueIdLength ==
|
||
extension->SystemPartitionUniqueId->UniqueIdLength &&
|
||
RtlCompareMemory(uniqueId->UniqueId,
|
||
extension->SystemPartitionUniqueId->UniqueId,
|
||
uniqueId->UniqueIdLength) ==
|
||
uniqueId->UniqueIdLength) {
|
||
|
||
IoSetSystemPartition(&targetName);
|
||
}
|
||
|
||
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,
|
||
isStable, &stableGuid);
|
||
|
||
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 = GlobalCreateSymbolicLink(&symbolicLinkNames[i], &targetName);
|
||
if (!NT_SUCCESS(status)) {
|
||
isLinkPreset = TRUE;
|
||
if (!savedLinks ||
|
||
!RedirectSavedLink(savedLinks, &symbolicLinkNames[i],
|
||
&targetName)) {
|
||
|
||
status = QueryDeviceInformation(&symbolicLinkNames[i],
|
||
&otherTargetName, NULL, NULL,
|
||
NULL, 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) {
|
||
GlobalDeleteSymbolicLink(&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) {
|
||
MountMgrFreeSavedLink (savedLinks);
|
||
}
|
||
|
||
if (!hasVolumeName) {
|
||
status = CreateNewVolumeName(&volumeName, NULL);
|
||
if (NT_SUCCESS(status)) {
|
||
RtlWriteRegistryValue(RTL_REGISTRY_ABSOLUTE,
|
||
MOUNTED_DEVICES_KEY, volumeName.Buffer, REG_BINARY,
|
||
uniqueId->UniqueId, uniqueId->UniqueIdLength);
|
||
|
||
GlobalCreateSymbolicLink(&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->AutoMountPermitted || deviceInfo->IsRemovable) &&
|
||
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);
|
||
}
|
||
} else {
|
||
CreateNoDriveLetterEntry(uniqueId);
|
||
}
|
||
}
|
||
|
||
if (!NotAPdo) {
|
||
RegisterForTargetDeviceNotification(extension, deviceInfo);
|
||
}
|
||
|
||
InsertTailList(&extension->MountedDeviceList, &deviceInfo->ListEntry);
|
||
|
||
allocSize = FIELD_OFFSET(MOUNTDEV_UNIQUE_ID, UniqueId) +
|
||
uniqueId->UniqueIdLength;
|
||
uniqueIdCopy = ExAllocatePool(PagedPool, allocSize);
|
||
if (uniqueIdCopy) {
|
||
RtlCopyMemory(uniqueIdCopy, uniqueId, allocSize);
|
||
}
|
||
|
||
if (offline || isFT) {
|
||
deviceInfo->InOfflineList = TRUE;
|
||
}
|
||
|
||
if (extension->AutoMountPermitted || hasDriveLetter) {
|
||
if (deviceInfo->InOfflineList) {
|
||
sendOnline = FALSE;
|
||
} else {
|
||
sendOnline = TRUE;
|
||
}
|
||
} else {
|
||
sendOnline = FALSE;
|
||
}
|
||
|
||
KeReleaseSemaphore(&extension->Mutex, IO_NO_INCREMENT, 1, FALSE);
|
||
|
||
if (sendOnline) {
|
||
SendOnlineNotification(NotificationName);
|
||
}
|
||
|
||
if (symbolicLinkNames) {
|
||
ExFreePool(symbolicLinkNames);
|
||
}
|
||
|
||
if (uniqueIdCopy) {
|
||
IssueUniqueIdChangeNotify(extension, NotificationName,
|
||
uniqueIdCopy);
|
||
ExFreePool(uniqueIdCopy);
|
||
}
|
||
|
||
if (extension->AutomaticDriveLetterAssignment) {
|
||
KeWaitForSingleObject(&extension->Mutex, Executive, KernelMode,
|
||
FALSE, NULL);
|
||
|
||
ReconcileThisDatabaseWithMaster(extension, deviceInfo);
|
||
|
||
for (l = extension->MountedDeviceList.Flink;
|
||
l != &extension->MountedDeviceList; l = l->Flink) {
|
||
|
||
d = CONTAINING_RECORD(l, MOUNTED_DEVICE_INFORMATION, ListEntry);
|
||
if (d->HasDanglingVolumeMountPoint) {
|
||
ReconcileThisDatabaseWithMaster(extension, d);
|
||
}
|
||
}
|
||
|
||
KeReleaseSemaphore(&extension->Mutex, IO_NO_INCREMENT, 1, FALSE);
|
||
}
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
VOID
|
||
MountMgrFreeMountedDeviceInfo(
|
||
IN PMOUNTED_DEVICE_INFORMATION DeviceInfo
|
||
)
|
||
|
||
{
|
||
|
||
PLIST_ENTRY l;
|
||
PSYMBOLIC_LINK_NAME_ENTRY symlinkEntry;
|
||
PREPLICATED_UNIQUE_ID replUniqueId;
|
||
PMOUNTMGR_MOUNT_POINT_ENTRY mountPointEntry;
|
||
|
||
while (!IsListEmpty(&DeviceInfo->SymbolicLinkNames)) {
|
||
|
||
l = RemoveHeadList(&DeviceInfo->SymbolicLinkNames);
|
||
symlinkEntry = CONTAINING_RECORD(l, SYMBOLIC_LINK_NAME_ENTRY,
|
||
ListEntry);
|
||
|
||
GlobalDeleteSymbolicLink(&symlinkEntry->SymbolicLinkName);
|
||
ExFreePool(symlinkEntry->SymbolicLinkName.Buffer);
|
||
ExFreePool(symlinkEntry);
|
||
}
|
||
|
||
while (!IsListEmpty(&DeviceInfo->ReplicatedUniqueIds)) {
|
||
|
||
l = RemoveHeadList(&DeviceInfo->ReplicatedUniqueIds);
|
||
replUniqueId = CONTAINING_RECORD(l, REPLICATED_UNIQUE_ID,
|
||
ListEntry);
|
||
|
||
ExFreePool(replUniqueId->UniqueId);
|
||
ExFreePool(replUniqueId);
|
||
}
|
||
|
||
while (!IsListEmpty(&DeviceInfo->MountPointsPointingHere)) {
|
||
|
||
l = RemoveHeadList(&DeviceInfo->MountPointsPointingHere);
|
||
mountPointEntry = CONTAINING_RECORD(l, MOUNTMGR_MOUNT_POINT_ENTRY,
|
||
ListEntry);
|
||
ExFreePool(mountPointEntry->MountPath.Buffer);
|
||
ExFreePool(mountPointEntry);
|
||
}
|
||
|
||
ExFreePool(DeviceInfo->NotificationName.Buffer);
|
||
|
||
if (!DeviceInfo->KeepLinksWhenOffline) {
|
||
ExFreePool(DeviceInfo->UniqueId);
|
||
}
|
||
|
||
ExFreePool(DeviceInfo->DeviceName.Buffer);
|
||
|
||
if (DeviceInfo->TargetDeviceNotificationEntry) {
|
||
IoUnregisterPlugPlayNotification(
|
||
DeviceInfo->TargetDeviceNotificationEntry);
|
||
}
|
||
|
||
ExFreePool(DeviceInfo);
|
||
}
|
||
|
||
NTSTATUS
|
||
MountMgrMountedDeviceRemoval(
|
||
IN PDEVICE_EXTENSION Extension,
|
||
IN PUNICODE_STRING NotificationName
|
||
)
|
||
|
||
{
|
||
PDEVICE_EXTENSION extension = Extension;
|
||
PMOUNTED_DEVICE_INFORMATION deviceInfo, d;
|
||
PSYMBOLIC_LINK_NAME_ENTRY symlinkEntry;
|
||
PLIST_ENTRY l, ll, s;
|
||
PREPLICATED_UNIQUE_ID replUniqueId;
|
||
PSAVED_LINKS_INFORMATION savedLinks;
|
||
PMOUNTMGR_MOUNT_POINT_ENTRY mountPointEntry;
|
||
|
||
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 {
|
||
GlobalDeleteSymbolicLink(&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);
|
||
|
||
GlobalDeleteSymbolicLink(&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);
|
||
}
|
||
|
||
while (!IsListEmpty(&deviceInfo->MountPointsPointingHere)) {
|
||
|
||
ll = RemoveHeadList(&deviceInfo->MountPointsPointingHere);
|
||
mountPointEntry = CONTAINING_RECORD(ll, MOUNTMGR_MOUNT_POINT_ENTRY,
|
||
ListEntry);
|
||
|
||
mountPointEntry->DeviceInfo->HasDanglingVolumeMountPoint = TRUE;
|
||
|
||
ExFreePool(mountPointEntry->MountPath.Buffer);
|
||
ExFreePool(mountPointEntry);
|
||
}
|
||
|
||
RemoveEntryList(l);
|
||
|
||
for (l = Extension->MountedDeviceList.Flink;
|
||
l != &Extension->MountedDeviceList; l = l->Flink) {
|
||
|
||
d = CONTAINING_RECORD(l, MOUNTED_DEVICE_INFORMATION, ListEntry);
|
||
|
||
for (ll = d->MountPointsPointingHere.Flink;
|
||
ll != &d->MountPointsPointingHere; ll = ll->Flink) {
|
||
|
||
mountPointEntry = CONTAINING_RECORD(ll, MOUNTMGR_MOUNT_POINT_ENTRY,
|
||
ListEntry);
|
||
if (mountPointEntry->DeviceInfo == deviceInfo) {
|
||
s = ll->Blink;
|
||
RemoveEntryList(ll);
|
||
ExFreePool(mountPointEntry->MountPath.Buffer);
|
||
ExFreePool(mountPointEntry);
|
||
ll = s;
|
||
}
|
||
}
|
||
}
|
||
|
||
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);
|
||
MountMgrFreeDeadDeviceInfo (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.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS
|
||
|
||
--*/
|
||
|
||
{
|
||
PDEVICE_INTERFACE_CHANGE_NOTIFICATION notification = NotificationStructure;
|
||
PDEVICE_EXTENSION extension = Extension;
|
||
BOOLEAN oldHardErrorMode;
|
||
NTSTATUS status;
|
||
|
||
oldHardErrorMode = PsGetThreadHardErrorsAreDisabled(PsGetCurrentThread());
|
||
PsSetThreadHardErrorsAreDisabled(PsGetCurrentThread(),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;
|
||
}
|
||
|
||
PsSetThreadHardErrorsAreDisabled(PsGetCurrentThread(),oldHardErrorMode);
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
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.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS
|
||
|
||
--*/
|
||
|
||
{
|
||
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, 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.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS
|
||
|
||
--*/
|
||
|
||
{
|
||
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, 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.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS
|
||
|
||
--*/
|
||
|
||
{
|
||
PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp);
|
||
PMOUNTMGR_MOUNT_POINT input;
|
||
LONGLONG 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;
|
||
}
|
||
|
||
if ((input->SymbolicLinkNameOffset&1) ||
|
||
(input->SymbolicLinkNameLength&1) ||
|
||
(input->UniqueIdOffset&1) ||
|
||
(input->UniqueIdLength&1) ||
|
||
(input->DeviceNameOffset&1) ||
|
||
(input->DeviceNameLength&1)) {
|
||
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
len1 = (LONGLONG) input->SymbolicLinkNameOffset +
|
||
input->SymbolicLinkNameLength;
|
||
len2 = (LONGLONG) input->UniqueIdOffset + input->UniqueIdLength;
|
||
len3 = (LONGLONG) 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.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS status;
|
||
PFILE_OBJECT fileObject;
|
||
PDEVICE_OBJECT deviceObject = NULL;
|
||
ULONG inputSize = sizeof(USHORT) + SymbolicLinkName->Length;
|
||
PMOUNTDEV_NAME input = NULL;
|
||
BOOLEAN objectsReferenced = FALSE;
|
||
KEVENT event;
|
||
PIRP irp;
|
||
IO_STATUS_BLOCK ioStatus;
|
||
PIO_STACK_LOCATION irpSp;
|
||
|
||
|
||
status = IoGetDeviceObjectPointer (DeviceName,
|
||
FILE_READ_ATTRIBUTES,
|
||
&fileObject,
|
||
&deviceObject);
|
||
|
||
if (NT_SUCCESS (status)) {
|
||
|
||
deviceObject = IoGetAttachedDeviceReference (fileObject->DeviceObject);
|
||
|
||
objectsReferenced = TRUE;
|
||
|
||
|
||
input = ExAllocatePool (PagedPool, inputSize);
|
||
|
||
if (NULL == input) {
|
||
status = STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
}
|
||
|
||
|
||
|
||
if (NT_SUCCESS (status)) {
|
||
input->NameLength = SymbolicLinkName->Length;
|
||
|
||
RtlCopyMemory (input->Name,
|
||
SymbolicLinkName->Buffer,
|
||
SymbolicLinkName->Length);
|
||
|
||
|
||
|
||
/*
|
||
** First send the notification using the standard IOCTL. When
|
||
** that's done we'll send another using the obsolete IOCTL for
|
||
** all those third parties who have yet to recompile.
|
||
*/
|
||
KeInitializeEvent(&event, NotificationEvent, FALSE);
|
||
|
||
irp = IoBuildDeviceIoControlRequest (IOCTL_MOUNTDEV_LINK_DELETED,
|
||
deviceObject,
|
||
input,
|
||
inputSize,
|
||
NULL,
|
||
0,
|
||
FALSE,
|
||
&event,
|
||
&ioStatus);
|
||
|
||
if (NULL != irp) {
|
||
|
||
irpSp = IoGetNextIrpStackLocation (irp);
|
||
irpSp->FileObject = fileObject;
|
||
|
||
status = IoCallDriver (deviceObject, irp);
|
||
|
||
if (status == STATUS_PENDING) {
|
||
KeWaitForSingleObject (&event, Executive, KernelMode, FALSE, NULL);
|
||
}
|
||
}
|
||
|
||
|
||
|
||
/*
|
||
** Now the 'obsolete' non-protected IOCTL
|
||
*/
|
||
KeInitializeEvent(&event, NotificationEvent, FALSE);
|
||
|
||
irp = IoBuildDeviceIoControlRequest (IOCTL_MOUNTDEV_LINK_DELETED_OBSOLETE,
|
||
deviceObject,
|
||
input,
|
||
inputSize,
|
||
NULL,
|
||
0,
|
||
FALSE,
|
||
&event,
|
||
&ioStatus);
|
||
|
||
if (NULL != irp) {
|
||
|
||
irpSp = IoGetNextIrpStackLocation (irp);
|
||
irpSp->FileObject = fileObject;
|
||
|
||
status = IoCallDriver (deviceObject, irp);
|
||
|
||
if (status == STATUS_PENDING) {
|
||
KeWaitForSingleObject (&event, Executive, KernelMode, FALSE, NULL);
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
|
||
if (NULL != input) {
|
||
ExFreePool (input);
|
||
}
|
||
|
||
if (objectsReferenced) {
|
||
ObDereferenceObject (deviceObject);
|
||
ObDereferenceObject (fileObject);
|
||
}
|
||
|
||
|
||
return;
|
||
}
|
||
|
||
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.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
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);
|
||
}
|
||
}
|
||
|
||
GlobalDeleteSymbolicLink(&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.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS
|
||
|
||
--*/
|
||
|
||
{
|
||
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.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
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 (IsFTPartition(partInfo.PartitionType)) {
|
||
return TRUE;
|
||
}
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
NTSTATUS
|
||
MountMgrNextDriveLetterWorker(
|
||
IN OUT PDEVICE_EXTENSION Extension,
|
||
IN PUNICODE_STRING DeviceName,
|
||
OUT PMOUNTMGR_DRIVE_LETTER_INFORMATION DriveLetterInfo
|
||
)
|
||
|
||
{
|
||
UNICODE_STRING deviceName = *DeviceName;
|
||
PMOUNTMGR_DRIVE_LETTER_INFORMATION output = DriveLetterInfo;
|
||
UNICODE_STRING targetName;
|
||
NTSTATUS status;
|
||
BOOLEAN isRecognized, isRemovable;
|
||
PLIST_ENTRY l;
|
||
PMOUNTED_DEVICE_INFORMATION deviceInfo;
|
||
PSYMBOLIC_LINK_NAME_ENTRY symlinkEntry;
|
||
UNICODE_STRING symbolicLinkName;
|
||
WCHAR symNameBuffer[30];
|
||
UCHAR startDriveLetterName;
|
||
PMOUNTDEV_UNIQUE_ID uniqueId;
|
||
|
||
if (!Extension->SuggestedDriveLettersProcessed) {
|
||
ProcessSuggestedDriveLetters(Extension);
|
||
Extension->SuggestedDriveLettersProcessed = TRUE;
|
||
}
|
||
|
||
status = QueryDeviceInformation(&deviceName, &targetName, NULL,
|
||
&isRemovable, &isRecognized, 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;
|
||
}
|
||
}
|
||
|
||
if (l == &Extension->MountedDeviceList) {
|
||
ExFreePool(targetName.Buffer);
|
||
return STATUS_OBJECT_NAME_NOT_FOUND;
|
||
}
|
||
|
||
deviceInfo->NextDriveLetterCalled = TRUE;
|
||
|
||
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) &&
|
||
symlinkEntry->IsInDatabase) {
|
||
|
||
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;
|
||
ExFreePool(targetName.Buffer);
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
if (!Extension->AutoMountPermitted && !isRemovable) {
|
||
if (output->DriveLetterWasAssigned) {
|
||
output->DriveLetterWasAssigned = FALSE;
|
||
output->CurrentDriveLetter = 0;
|
||
}
|
||
ExFreePool(targetName.Buffer);
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
if (!output->DriveLetterWasAssigned) {
|
||
ExFreePool(targetName.Buffer);
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
if (RtlPrefixUnicodeString(&DeviceFloppy, &targetName, TRUE)) {
|
||
startDriveLetterName = 'A';
|
||
} else if (RtlPrefixUnicodeString(&DeviceCdRom, &targetName, TRUE)) {
|
||
startDriveLetterName = 'D';
|
||
} else {
|
||
startDriveLetterName = 'C';
|
||
}
|
||
|
||
ASSERT(deviceInfo->SuggestedDriveLetter != 0xFF);
|
||
|
||
if (!deviceInfo->SuggestedDriveLetter &&
|
||
IsFtVolume(&deviceInfo->DeviceName)) {
|
||
|
||
output->DriveLetterWasAssigned = FALSE;
|
||
output->CurrentDriveLetter = 0;
|
||
ExFreePool(targetName.Buffer);
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
symbolicLinkName.Length = symbolicLinkName.MaximumLength = 28;
|
||
symbolicLinkName.Buffer = symNameBuffer;
|
||
RtlCopyMemory(symbolicLinkName.Buffer, DosDevices.Buffer, 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)) {
|
||
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;
|
||
status = QueryDeviceInformation(&targetName, NULL, &uniqueId,
|
||
NULL, NULL, NULL, NULL, NULL);
|
||
if (NT_SUCCESS(status)) {
|
||
CreateNoDriveLetterEntry(uniqueId);
|
||
ExFreePool(uniqueId);
|
||
}
|
||
}
|
||
|
||
ExFreePool(targetName.Buffer);
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
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.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS
|
||
|
||
--*/
|
||
|
||
{
|
||
PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp);
|
||
PMOUNTMGR_DRIVE_LETTER_TARGET input;
|
||
UNICODE_STRING deviceName;
|
||
NTSTATUS status;
|
||
MOUNTMGR_DRIVE_LETTER_INFORMATION driveLetterInfo;
|
||
PMOUNTMGR_DRIVE_LETTER_INFORMATION output;
|
||
|
||
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;
|
||
}
|
||
|
||
deviceName.MaximumLength = deviceName.Length = input->DeviceNameLength;
|
||
deviceName.Buffer = input->DeviceName;
|
||
|
||
status = MountMgrNextDriveLetterWorker(Extension, &deviceName,
|
||
&driveLetterInfo);
|
||
if (!NT_SUCCESS(status)) {
|
||
return status;
|
||
}
|
||
|
||
output = Irp->AssociatedIrp.SystemBuffer;
|
||
*output = driveLetterInfo;
|
||
|
||
Irp->IoStatus.Information = sizeof(MOUNTMGR_DRIVE_LETTER_INFORMATION);
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
NTSTATUS
|
||
MountMgrVolumeMountPointChanged(
|
||
IN OUT PDEVICE_EXTENSION Extension,
|
||
IN OUT PIRP Irp,
|
||
IN NTSTATUS ResultOfWaitForDatabase,
|
||
OUT PUNICODE_STRING SourceVolume,
|
||
OUT PUNICODE_STRING MountPath,
|
||
OUT PUNICODE_STRING TargetVolume
|
||
)
|
||
|
||
{
|
||
PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp);
|
||
PMOUNTMGR_VOLUME_MOUNT_POINT input = Irp->AssociatedIrp.SystemBuffer;
|
||
OBJECT_ATTRIBUTES oa;
|
||
NTSTATUS status = STATUS_SUCCESS;
|
||
HANDLE h = NULL;
|
||
IO_STATUS_BLOCK ioStatus;
|
||
PFILE_OBJECT fileObject;
|
||
UNICODE_STRING deviceName;
|
||
UNICODE_STRING sourceVolumeName;
|
||
FILE_FS_DEVICE_INFORMATION fsDeviceInformation;
|
||
OBJECT_NAME_INFORMATION tempObjectNameInfo;
|
||
PFILE_NAME_INFORMATION fileNameInformation = NULL;
|
||
POBJECT_NAME_INFORMATION objectNameInformation = NULL;
|
||
PMOUNTED_DEVICE_INFORMATION deviceInfo = NULL;
|
||
BOOLEAN fileObjectReferenced = FALSE;
|
||
ULONG returnedLength = 0;
|
||
ULONG allocationSize;
|
||
|
||
|
||
deviceName.Buffer = NULL;
|
||
|
||
|
||
if ((irpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof (MOUNTMGR_VOLUME_MOUNT_POINT)) ||
|
||
(irpSp->Parameters.DeviceIoControl.InputBufferLength <
|
||
(ULONG) MAX (input->SourceVolumeNameOffset + input->SourceVolumeNameLength,
|
||
input->TargetVolumeNameOffset + input->TargetVolumeNameLength))) {
|
||
status = STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
|
||
if (NT_SUCCESS (status)) {
|
||
sourceVolumeName.Length = input->SourceVolumeNameLength;
|
||
sourceVolumeName.MaximumLength = input->SourceVolumeNameLength;
|
||
sourceVolumeName.Buffer = (PWSTR) ((PCHAR) input + input->SourceVolumeNameOffset);
|
||
|
||
InitializeObjectAttributes (&oa,
|
||
&sourceVolumeName,
|
||
OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE,
|
||
0,
|
||
0);
|
||
|
||
status = ZwOpenFile (&h,
|
||
FILE_READ_ATTRIBUTES | SYNCHRONIZE,
|
||
&oa,
|
||
&ioStatus,
|
||
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
||
FILE_OPEN_REPARSE_POINT | FILE_SYNCHRONOUS_IO_NONALERT);
|
||
|
||
|
||
if (!NT_SUCCESS (status)) {
|
||
h = NULL;
|
||
}
|
||
}
|
||
|
||
|
||
if (NT_SUCCESS (status)) {
|
||
status = ZwQueryVolumeInformationFile (h,
|
||
&ioStatus,
|
||
&fsDeviceInformation,
|
||
sizeof (fsDeviceInformation),
|
||
FileFsDeviceInformation);
|
||
}
|
||
|
||
|
||
if (NT_SUCCESS (status) &&
|
||
(!((FILE_DEVICE_DISK == fsDeviceInformation.DeviceType) ||
|
||
(FILE_DEVICE_VIRTUAL_DISK == fsDeviceInformation.DeviceType)) ||
|
||
((FILE_REMOVABLE_MEDIA | FILE_REMOTE_DEVICE) & fsDeviceInformation.Characteristics))) {
|
||
/*
|
||
** If the device is
|
||
** not a disk or virtual disk
|
||
** a remote file
|
||
** based on removable media,
|
||
**
|
||
** then we are going to dis-allow the operation.
|
||
*/
|
||
status = STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
|
||
if (NT_SUCCESS (status)) {
|
||
status = ObReferenceObjectByHandle (h,
|
||
0,
|
||
*IoFileObjectType,
|
||
KernelMode,
|
||
(PVOID*) &fileObject,
|
||
NULL);
|
||
|
||
fileObjectReferenced = NT_SUCCESS (status);
|
||
}
|
||
|
||
|
||
|
||
if (NT_SUCCESS (status)) {
|
||
|
||
allocationSize = sizeof (FILE_NAME_INFORMATION);
|
||
fileNameInformation = ExAllocatePool (PagedPool, allocationSize);
|
||
|
||
status = (NULL == fileNameInformation) ? STATUS_INSUFFICIENT_RESOURCES : STATUS_SUCCESS;
|
||
}
|
||
|
||
|
||
if (NT_SUCCESS (status)) {
|
||
|
||
status = ZwQueryInformationFile (h,
|
||
&ioStatus,
|
||
fileNameInformation,
|
||
allocationSize,
|
||
FileNameInformation);
|
||
|
||
if (STATUS_BUFFER_OVERFLOW == status) {
|
||
|
||
allocationSize = sizeof (FILE_NAME_INFORMATION) + fileNameInformation->FileNameLength;
|
||
|
||
ExFreePool (fileNameInformation);
|
||
|
||
fileNameInformation = ExAllocatePool (PagedPool, allocationSize);
|
||
|
||
status = (NULL == fileNameInformation) ? STATUS_INSUFFICIENT_RESOURCES : STATUS_SUCCESS;
|
||
}
|
||
}
|
||
|
||
|
||
|
||
if (NT_SUCCESS (status)) {
|
||
status = ZwQueryInformationFile (h,
|
||
&ioStatus,
|
||
fileNameInformation,
|
||
allocationSize,
|
||
FileNameInformation);
|
||
}
|
||
|
||
|
||
|
||
if (NT_SUCCESS (status)) {
|
||
|
||
objectNameInformation = &tempObjectNameInfo;
|
||
allocationSize = sizeof (tempObjectNameInfo);
|
||
|
||
status = ObQueryNameString (fileObject->DeviceObject,
|
||
objectNameInformation,
|
||
allocationSize,
|
||
&returnedLength);
|
||
|
||
if (STATUS_INFO_LENGTH_MISMATCH == status) {
|
||
|
||
allocationSize = returnedLength;
|
||
|
||
objectNameInformation = ExAllocatePool (PagedPool, allocationSize);
|
||
|
||
status = (NULL == objectNameInformation) ? STATUS_INSUFFICIENT_RESOURCES : STATUS_SUCCESS;
|
||
}
|
||
}
|
||
|
||
|
||
|
||
if (NT_SUCCESS (status)) {
|
||
status = ObQueryNameString (fileObject->DeviceObject,
|
||
objectNameInformation,
|
||
allocationSize,
|
||
&returnedLength);
|
||
}
|
||
|
||
|
||
|
||
if (NT_SUCCESS (status)) {
|
||
status = QueryDeviceInformation (&objectNameInformation->Name,
|
||
SourceVolume,
|
||
NULL,
|
||
NULL,
|
||
NULL,
|
||
NULL,
|
||
NULL,
|
||
NULL);
|
||
}
|
||
|
||
|
||
|
||
if (NT_SUCCESS (status)) {
|
||
TargetVolume->Length = input->TargetVolumeNameLength;
|
||
TargetVolume->MaximumLength = input->TargetVolumeNameLength;
|
||
TargetVolume->Buffer = (PWSTR) ((PCHAR) input + input->TargetVolumeNameOffset);
|
||
|
||
status = QueryDeviceInformation (TargetVolume,
|
||
&deviceName,
|
||
NULL,
|
||
NULL,
|
||
NULL,
|
||
NULL,
|
||
NULL,
|
||
NULL);
|
||
}
|
||
|
||
|
||
|
||
if (NT_SUCCESS (status)) {
|
||
MountPath->Length = (USHORT) fileNameInformation->FileNameLength;
|
||
MountPath->MaximumLength = (USHORT) fileNameInformation->FileNameLength;
|
||
MountPath->Buffer = (PVOID) fileNameInformation;
|
||
|
||
RtlMoveMemory (fileNameInformation, fileNameInformation->FileName, MountPath->Length);
|
||
fileNameInformation = NULL;
|
||
|
||
MountMgrNotify (Extension);
|
||
MountMgrNotifyNameChange (Extension, &deviceName, TRUE);
|
||
|
||
if (!NT_SUCCESS (ResultOfWaitForDatabase)) {
|
||
status = FindDeviceInfo (Extension,
|
||
SourceVolume,
|
||
FALSE,
|
||
&deviceInfo);
|
||
|
||
if (NT_SUCCESS(status)) {
|
||
ReconcileThisDatabaseWithMaster (Extension, deviceInfo);
|
||
} else {
|
||
status = STATUS_PENDING;
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
|
||
if (NULL != deviceName.Buffer) {
|
||
ExFreePool (deviceName.Buffer);
|
||
}
|
||
|
||
if ((NULL != objectNameInformation) &&
|
||
(&tempObjectNameInfo != objectNameInformation)) {
|
||
ExFreePool (objectNameInformation);
|
||
}
|
||
|
||
if (NULL != fileNameInformation) {
|
||
ExFreePool (fileNameInformation);
|
||
}
|
||
|
||
if (fileObjectReferenced) {
|
||
ObDereferenceObject (fileObject);
|
||
}
|
||
|
||
if (NULL != h) {
|
||
ZwClose (h);
|
||
}
|
||
|
||
return (status);
|
||
}
|
||
|
||
NTSTATUS
|
||
MountMgrQuerySymbolicLink(
|
||
IN PUNICODE_STRING SourceOfLink,
|
||
IN OUT PUNICODE_STRING TargetOfLink
|
||
)
|
||
|
||
{
|
||
OBJECT_ATTRIBUTES oa;
|
||
NTSTATUS status;
|
||
HANDLE handle;
|
||
|
||
InitializeObjectAttributes(&oa, SourceOfLink, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, 0, 0);
|
||
|
||
status = ZwOpenSymbolicLinkObject(&handle, GENERIC_READ, &oa);
|
||
if (!NT_SUCCESS(status)) {
|
||
return status;
|
||
}
|
||
|
||
status = ZwQuerySymbolicLinkObject(handle, TargetOfLink, NULL);
|
||
ZwClose(handle);
|
||
|
||
if (NT_SUCCESS(status)) {
|
||
if (TargetOfLink->Length > 1*sizeof(WCHAR) &&
|
||
TargetOfLink->Buffer[TargetOfLink->Length/sizeof(WCHAR) - 1] ==
|
||
'\\') {
|
||
|
||
TargetOfLink->Length -= sizeof(WCHAR);
|
||
TargetOfLink->Buffer[TargetOfLink->Length/sizeof(WCHAR)] = 0;
|
||
}
|
||
}
|
||
|
||
return status;
|
||
}
|
||
|
||
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.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS status;
|
||
UNICODE_STRING sourceVolume, mountPath, targetVolume, v, p;
|
||
PMOUNTED_DEVICE_INFORMATION sourceDeviceInfo, targetDeviceInfo;
|
||
HANDLE h;
|
||
ULONG offset;
|
||
BOOLEAN entryFound;
|
||
PMOUNTMGR_FILE_ENTRY databaseEntry;
|
||
UNICODE_STRING otherTargetVolumeName;
|
||
PMOUNTDEV_UNIQUE_ID uniqueId;
|
||
ULONG size;
|
||
PREPLICATED_UNIQUE_ID replUniqueId;
|
||
PMOUNTMGR_MOUNT_POINT_ENTRY mountPointEntry;
|
||
|
||
v.MaximumLength = MAX_VOLUME_PATH*sizeof(WCHAR);
|
||
v.Length = 0;
|
||
v.Buffer = ExAllocatePool (PagedPool, v.MaximumLength);
|
||
|
||
if (NULL == v.Buffer) {
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
|
||
status = MountMgrVolumeMountPointChanged(Extension, Irp,
|
||
ResultOfWaitForDatabase,
|
||
&sourceVolume, &mountPath,
|
||
&targetVolume);
|
||
if (status == STATUS_PENDING) {
|
||
ExFreePool (v.Buffer);
|
||
ExFreePool (sourceVolume.Buffer);
|
||
ExFreePool (mountPath.Buffer);
|
||
return STATUS_SUCCESS;
|
||
}
|
||
if (!NT_SUCCESS(status)) {
|
||
ExFreePool (v.Buffer);
|
||
return status;
|
||
}
|
||
|
||
status = FindDeviceInfo(Extension, &sourceVolume, FALSE,
|
||
&sourceDeviceInfo);
|
||
if (!NT_SUCCESS(status)) {
|
||
|
||
status = QueryVolumeName(NULL, NULL, &sourceVolume, &v, &p);
|
||
if (NT_SUCCESS(status)) {
|
||
ExFreePool(p.Buffer);
|
||
} else {
|
||
status = MountMgrQuerySymbolicLink(&sourceVolume, &v);
|
||
if (!NT_SUCCESS(status)) {
|
||
ExFreePool (v.Buffer);
|
||
ExFreePool (sourceVolume.Buffer);
|
||
ExFreePool (mountPath.Buffer);
|
||
return status;
|
||
}
|
||
}
|
||
|
||
ExFreePool (sourceVolume.Buffer);
|
||
sourceVolume = v;
|
||
|
||
status = FindDeviceInfo(Extension, &sourceVolume, FALSE,
|
||
&sourceDeviceInfo);
|
||
if (!NT_SUCCESS(status)) {
|
||
ExFreePool (sourceVolume.Buffer);
|
||
ExFreePool (mountPath.Buffer);
|
||
return status;
|
||
}
|
||
}
|
||
|
||
|
||
ExFreePool (sourceVolume.Buffer);
|
||
|
||
|
||
status = FindDeviceInfo(Extension, &targetVolume, FALSE,
|
||
&targetDeviceInfo);
|
||
if (!NT_SUCCESS(status)) {
|
||
ExFreePool (mountPath.Buffer);
|
||
return status;
|
||
}
|
||
|
||
if (!targetDeviceInfo->InOfflineList) {
|
||
PostOnlineNotification(&targetDeviceInfo->NotificationName);
|
||
}
|
||
|
||
h = OpenRemoteDatabase(sourceDeviceInfo, TRUE);
|
||
if (!h) {
|
||
ExFreePool (mountPath.Buffer);
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
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(&targetVolume, &otherTargetVolumeName,
|
||
TRUE)) {
|
||
|
||
entryFound = TRUE;
|
||
break;
|
||
}
|
||
|
||
offset += databaseEntry->EntryLength;
|
||
ExFreePool(databaseEntry);
|
||
}
|
||
|
||
if (entryFound) {
|
||
|
||
databaseEntry->RefCount++;
|
||
status = WriteRemoteDatabaseEntry(h, offset, databaseEntry);
|
||
ExFreePool(databaseEntry);
|
||
|
||
} else {
|
||
|
||
status = QueryDeviceInformation(&targetVolume, NULL, &uniqueId, NULL,
|
||
NULL, NULL, NULL, NULL);
|
||
if (!NT_SUCCESS(status)) {
|
||
ExFreePool (mountPath.Buffer);
|
||
CloseRemoteDatabase(h);
|
||
return status;
|
||
}
|
||
|
||
size = sizeof(MOUNTMGR_FILE_ENTRY) + targetVolume.Length +
|
||
uniqueId->UniqueIdLength;
|
||
|
||
databaseEntry = ExAllocatePool(PagedPool, size);
|
||
if (!databaseEntry) {
|
||
ExFreePool(uniqueId);
|
||
ExFreePool (mountPath.Buffer);
|
||
CloseRemoteDatabase(h);
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
databaseEntry->EntryLength = size;
|
||
databaseEntry->RefCount = 1;
|
||
databaseEntry->VolumeNameOffset = sizeof(MOUNTMGR_FILE_ENTRY);
|
||
databaseEntry->VolumeNameLength = targetVolume.Length;
|
||
databaseEntry->UniqueIdOffset = databaseEntry->VolumeNameOffset +
|
||
databaseEntry->VolumeNameLength;
|
||
databaseEntry->UniqueIdLength = uniqueId->UniqueIdLength;
|
||
|
||
RtlCopyMemory((PCHAR) databaseEntry + databaseEntry->VolumeNameOffset,
|
||
targetVolume.Buffer, databaseEntry->VolumeNameLength);
|
||
RtlCopyMemory((PCHAR) databaseEntry + databaseEntry->UniqueIdOffset,
|
||
uniqueId->UniqueId, databaseEntry->UniqueIdLength);
|
||
|
||
status = AddRemoteDatabaseEntry(h, databaseEntry);
|
||
|
||
ExFreePool(databaseEntry);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
ExFreePool(uniqueId);
|
||
ExFreePool (mountPath.Buffer);
|
||
CloseRemoteDatabase(h);
|
||
return status;
|
||
}
|
||
|
||
replUniqueId = ExAllocatePool(PagedPool, sizeof(REPLICATED_UNIQUE_ID));
|
||
if (!replUniqueId) {
|
||
ExFreePool(uniqueId);
|
||
CloseRemoteDatabase(h);
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
replUniqueId->UniqueId = uniqueId;
|
||
|
||
InsertTailList(&sourceDeviceInfo->ReplicatedUniqueIds,
|
||
&replUniqueId->ListEntry);
|
||
}
|
||
|
||
CloseRemoteDatabase(h);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
ExFreePool (mountPath.Buffer);
|
||
return status;
|
||
}
|
||
|
||
mountPointEntry = (PMOUNTMGR_MOUNT_POINT_ENTRY)
|
||
ExAllocatePool(PagedPool,
|
||
sizeof(MOUNTMGR_MOUNT_POINT_ENTRY));
|
||
if (!mountPointEntry) {
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
mountPointEntry->MountPath.Length = mountPath.Length;
|
||
mountPointEntry->MountPath.MaximumLength = mountPath.Length +
|
||
sizeof(WCHAR);
|
||
mountPointEntry->MountPath.Buffer =
|
||
ExAllocatePool(PagedPool,
|
||
mountPointEntry->MountPath.MaximumLength);
|
||
if (!mountPointEntry->MountPath.Buffer) {
|
||
ExFreePool(mountPointEntry);
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
RtlCopyMemory(mountPointEntry->MountPath.Buffer,
|
||
mountPath.Buffer, mountPath.Length);
|
||
mountPointEntry->MountPath.Buffer[mountPath.Length/sizeof(WCHAR)] = 0;
|
||
|
||
mountPointEntry->DeviceInfo = sourceDeviceInfo;
|
||
InsertTailList(&targetDeviceInfo->MountPointsPointingHere,
|
||
&mountPointEntry->ListEntry);
|
||
|
||
ExFreePool (mountPath.Buffer);
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
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.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS status;
|
||
UNICODE_STRING sourceVolume, mountPath, targetVolume, v, p;
|
||
PMOUNTED_DEVICE_INFORMATION sourceDeviceInfo, targetDeviceInfo;
|
||
HANDLE h;
|
||
ULONG offset;
|
||
BOOLEAN entryFound;
|
||
PMOUNTMGR_FILE_ENTRY databaseEntry;
|
||
UNICODE_STRING otherTargetVolumeName;
|
||
PLIST_ENTRY l;
|
||
PREPLICATED_UNIQUE_ID replUniqueId;
|
||
PMOUNTMGR_MOUNT_POINT_ENTRY mountPointEntry;
|
||
|
||
|
||
v.MaximumLength = MAX_VOLUME_PATH*sizeof(WCHAR);
|
||
v.Length = 0;
|
||
v.Buffer = ExAllocatePool (PagedPool, v.MaximumLength);
|
||
|
||
if (NULL == v.Buffer) {
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
|
||
status = MountMgrVolumeMountPointChanged(Extension, Irp,
|
||
ResultOfWaitForDatabase,
|
||
&sourceVolume, &mountPath,
|
||
&targetVolume);
|
||
if (status == STATUS_PENDING) {
|
||
ExFreePool (v.Buffer);
|
||
ExFreePool (sourceVolume.Buffer);
|
||
ExFreePool (mountPath.Buffer);
|
||
return STATUS_SUCCESS;
|
||
}
|
||
if (!NT_SUCCESS(status)) {
|
||
ExFreePool (v.Buffer);
|
||
return status;
|
||
}
|
||
|
||
status = FindDeviceInfo(Extension, &sourceVolume, FALSE,
|
||
&sourceDeviceInfo);
|
||
if (!NT_SUCCESS(status)) {
|
||
|
||
status = QueryVolumeName(NULL, NULL, &sourceVolume, &v, &p);
|
||
if (NT_SUCCESS(status)) {
|
||
ExFreePool(p.Buffer);
|
||
} else {
|
||
status = MountMgrQuerySymbolicLink(&sourceVolume, &v);
|
||
if (!NT_SUCCESS(status)) {
|
||
ExFreePool (v.Buffer);
|
||
ExFreePool (sourceVolume.Buffer);
|
||
ExFreePool (mountPath.Buffer);
|
||
return status;
|
||
}
|
||
}
|
||
|
||
ExFreePool (sourceVolume.Buffer);
|
||
sourceVolume = v;
|
||
|
||
status = FindDeviceInfo(Extension, &sourceVolume, FALSE,
|
||
&sourceDeviceInfo);
|
||
if (!NT_SUCCESS(status)) {
|
||
ExFreePool (sourceVolume.Buffer);
|
||
ExFreePool (mountPath.Buffer);
|
||
return status;
|
||
}
|
||
}
|
||
|
||
ExFreePool (sourceVolume.Buffer);
|
||
|
||
|
||
status = FindDeviceInfo(Extension, &targetVolume, FALSE,
|
||
&targetDeviceInfo);
|
||
if (!NT_SUCCESS(status)) {
|
||
ExFreePool (mountPath.Buffer);
|
||
return status;
|
||
}
|
||
|
||
h = OpenRemoteDatabase(sourceDeviceInfo, TRUE);
|
||
if (!h) {
|
||
ExFreePool (mountPath.Buffer);
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
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(&targetVolume, &otherTargetVolumeName,
|
||
TRUE)) {
|
||
|
||
entryFound = TRUE;
|
||
break;
|
||
}
|
||
|
||
offset += databaseEntry->EntryLength;
|
||
ExFreePool(databaseEntry);
|
||
}
|
||
|
||
if (!entryFound) {
|
||
ExFreePool (mountPath.Buffer);
|
||
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);
|
||
ExFreePool (mountPath.Buffer);
|
||
CloseRemoteDatabase(h);
|
||
return status;
|
||
}
|
||
|
||
for (l = sourceDeviceInfo->ReplicatedUniqueIds.Flink;
|
||
l != &sourceDeviceInfo->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 == &sourceDeviceInfo->ReplicatedUniqueIds) {
|
||
ExFreePool(databaseEntry);
|
||
ExFreePool (mountPath.Buffer);
|
||
CloseRemoteDatabase(h);
|
||
return STATUS_UNSUCCESSFUL;
|
||
}
|
||
|
||
RemoveEntryList(l);
|
||
ExFreePool(replUniqueId->UniqueId);
|
||
ExFreePool(replUniqueId);
|
||
}
|
||
|
||
ExFreePool(databaseEntry);
|
||
CloseRemoteDatabase(h);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
ExFreePool (mountPath.Buffer);
|
||
return status;
|
||
}
|
||
|
||
for (l = targetDeviceInfo->MountPointsPointingHere.Flink;
|
||
l != &targetDeviceInfo->MountPointsPointingHere; l = l->Flink) {
|
||
|
||
mountPointEntry = CONTAINING_RECORD(l, MOUNTMGR_MOUNT_POINT_ENTRY,
|
||
ListEntry);
|
||
|
||
if (mountPointEntry->DeviceInfo == sourceDeviceInfo &&
|
||
RtlEqualUnicodeString(&mountPointEntry->MountPath,
|
||
&mountPath, TRUE)) {
|
||
|
||
RemoveEntryList(l);
|
||
ExFreePool(mountPointEntry->MountPath.Buffer);
|
||
ExFreePool(mountPointEntry);
|
||
break;
|
||
}
|
||
}
|
||
|
||
ExFreePool (mountPath.Buffer);
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
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.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS
|
||
|
||
--*/
|
||
|
||
{
|
||
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.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
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 (deviceInfo->IsRemovable) {
|
||
continue;
|
||
}
|
||
|
||
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.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS
|
||
|
||
--*/
|
||
|
||
{
|
||
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);
|
||
MountMgrFreeDeadDeviceInfo (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.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS
|
||
|
||
--*/
|
||
|
||
{
|
||
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 = PsGetThreadHardErrorsAreDisabled(PsGetCurrentThread());
|
||
PsSetThreadHardErrorsAreDisabled(PsGetCurrentThread(),TRUE);
|
||
|
||
status = MountMgrMountedDeviceArrival(Extension, &deviceName, TRUE);
|
||
|
||
PsSetThreadHardErrorsAreDisabled(PsGetCurrentThread(),oldHardErrorMode);
|
||
|
||
return status;
|
||
}
|
||
|
||
NTSTATUS
|
||
MountMgrQuerySystemVolumeNameQueryRoutine(
|
||
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 system volume name.
|
||
|
||
EntryContext - Not used.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS
|
||
|
||
--*/
|
||
|
||
{
|
||
PUNICODE_STRING systemVolumeName = Context;
|
||
UNICODE_STRING string;
|
||
|
||
if (ValueType != REG_SZ) {
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
RtlInitUnicodeString(&string, ValueData);
|
||
|
||
systemVolumeName->Length = string.Length;
|
||
systemVolumeName->MaximumLength = systemVolumeName->Length + sizeof(WCHAR);
|
||
systemVolumeName->Buffer = ExAllocatePool(PagedPool,
|
||
systemVolumeName->MaximumLength);
|
||
if (!systemVolumeName->Buffer) {
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
RtlCopyMemory(systemVolumeName->Buffer, ValueData,
|
||
systemVolumeName->Length);
|
||
systemVolumeName->Buffer[systemVolumeName->Length/sizeof(WCHAR)] = 0;
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
NTSTATUS
|
||
MountMgrQuerySystemVolumeName(
|
||
OUT PUNICODE_STRING SystemVolumeName
|
||
)
|
||
|
||
{
|
||
RTL_QUERY_REGISTRY_TABLE queryTable[2];
|
||
|
||
RtlZeroMemory(queryTable, 2*sizeof(RTL_QUERY_REGISTRY_TABLE));
|
||
queryTable[0].QueryRoutine = MountMgrQuerySystemVolumeNameQueryRoutine;
|
||
queryTable[0].Flags = RTL_QUERY_REGISTRY_REQUIRED;
|
||
queryTable[0].Name = L"SystemPartition";
|
||
|
||
SystemVolumeName->Buffer = NULL;
|
||
|
||
RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
|
||
L"\\Registry\\Machine\\System\\Setup",
|
||
queryTable, SystemVolumeName, NULL);
|
||
|
||
if (!SystemVolumeName->Buffer) {
|
||
return STATUS_UNSUCCESSFUL;
|
||
}
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
VOID
|
||
MountMgrAssignDriveLetters(
|
||
IN PDEVICE_EXTENSION Extension
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is invoked after IoAssignDriveLetters has run. It goes
|
||
through all of the mounted devices and checks to see whether or not they
|
||
need to get a drive letter.
|
||
|
||
Arguments:
|
||
|
||
Extension - Supplies the device extension.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS status;
|
||
UNICODE_STRING systemVolumeName;
|
||
PLIST_ENTRY l;
|
||
PMOUNTED_DEVICE_INFORMATION deviceInfo;
|
||
MOUNTMGR_DRIVE_LETTER_INFORMATION driveLetterInfo;
|
||
|
||
status = MountMgrQuerySystemVolumeName(&systemVolumeName);
|
||
|
||
for (l = Extension->MountedDeviceList.Flink;
|
||
l != &Extension->MountedDeviceList; l = l->Flink) {
|
||
|
||
deviceInfo = CONTAINING_RECORD(l, MOUNTED_DEVICE_INFORMATION,
|
||
ListEntry);
|
||
if (!deviceInfo->NextDriveLetterCalled) {
|
||
MountMgrNextDriveLetterWorker(Extension, &deviceInfo->DeviceName,
|
||
&driveLetterInfo);
|
||
}
|
||
if (NT_SUCCESS(status) &&
|
||
RtlEqualUnicodeString(&systemVolumeName, &deviceInfo->DeviceName,
|
||
TRUE)) {
|
||
|
||
Extension->SystemPartitionUniqueId =
|
||
ExAllocatePool(PagedPool, sizeof(MOUNTDEV_UNIQUE_ID) +
|
||
deviceInfo->UniqueId->UniqueIdLength);
|
||
if (Extension->SystemPartitionUniqueId) {
|
||
Extension->SystemPartitionUniqueId->UniqueIdLength =
|
||
deviceInfo->UniqueId->UniqueIdLength;
|
||
RtlCopyMemory(Extension->SystemPartitionUniqueId->UniqueId,
|
||
deviceInfo->UniqueId->UniqueId,
|
||
deviceInfo->UniqueId->UniqueIdLength);
|
||
}
|
||
|
||
if (!Extension->AutoMountPermitted) {
|
||
Extension->AutoMountPermitted = TRUE;
|
||
MountMgrNextDriveLetterWorker(Extension,
|
||
&deviceInfo->DeviceName,
|
||
&driveLetterInfo);
|
||
Extension->AutoMountPermitted = FALSE;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (NT_SUCCESS(status)) {
|
||
ExFreePool(systemVolumeName.Buffer);
|
||
}
|
||
}
|
||
|
||
NTSTATUS
|
||
MountMgrValidateBackPointer(
|
||
IN PMOUNTMGR_MOUNT_POINT_ENTRY MountPointEntry,
|
||
IN PMOUNTED_DEVICE_INFORMATION DeviceInfo,
|
||
OUT PBOOLEAN InvalidBackPointer
|
||
)
|
||
|
||
{
|
||
UNICODE_STRING reparseName, volumeName;
|
||
OBJECT_ATTRIBUTES oa;
|
||
NTSTATUS status;
|
||
HANDLE h;
|
||
IO_STATUS_BLOCK ioStatus;
|
||
PREPARSE_DATA_BUFFER reparse;
|
||
PLIST_ENTRY l;
|
||
PSYMBOLIC_LINK_NAME_ENTRY symlinkEntry;
|
||
|
||
reparseName.Length = MountPointEntry->DeviceInfo->DeviceName.Length +
|
||
sizeof(WCHAR) + MountPointEntry->MountPath.Length;
|
||
reparseName.MaximumLength = reparseName.Length + sizeof(WCHAR);
|
||
reparseName.Buffer = ExAllocatePool(PagedPool, reparseName.MaximumLength);
|
||
if (!reparseName.Buffer) {
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
RtlCopyMemory(reparseName.Buffer,
|
||
MountPointEntry->DeviceInfo->DeviceName.Buffer,
|
||
MountPointEntry->DeviceInfo->DeviceName.Length);
|
||
reparseName.Length = MountPointEntry->DeviceInfo->DeviceName.Length;
|
||
reparseName.Buffer[reparseName.Length/sizeof(WCHAR)] = '\\';
|
||
reparseName.Length += sizeof(WCHAR);
|
||
RtlCopyMemory((PCHAR) reparseName.Buffer + reparseName.Length,
|
||
MountPointEntry->MountPath.Buffer,
|
||
MountPointEntry->MountPath.Length);
|
||
reparseName.Length += MountPointEntry->MountPath.Length;
|
||
reparseName.Buffer[reparseName.Length/sizeof(WCHAR)] = 0;
|
||
|
||
InitializeObjectAttributes(&oa, &reparseName, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, 0, 0);
|
||
|
||
status = ZwOpenFile(
|
||
&h,
|
||
FILE_READ_ATTRIBUTES | SYNCHRONIZE,
|
||
&oa,
|
||
&ioStatus,
|
||
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
||
FILE_OPEN_REPARSE_POINT | FILE_SYNCHRONOUS_IO_NONALERT
|
||
);
|
||
ExFreePool(reparseName.Buffer);
|
||
if (!NT_SUCCESS(status)) {
|
||
*InvalidBackPointer = TRUE;
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
reparse = ExAllocatePool(PagedPool, MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
|
||
if (!reparse) {
|
||
ZwClose(h);
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
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)) {
|
||
*InvalidBackPointer = TRUE;
|
||
ExFreePool(reparse);
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
volumeName.MaximumLength = volumeName.Length =
|
||
reparse->MountPointReparseBuffer.SubstituteNameLength;
|
||
volumeName.Buffer = (PWCHAR)
|
||
((PCHAR) reparse->MountPointReparseBuffer.PathBuffer +
|
||
reparse->MountPointReparseBuffer.SubstituteNameOffset);
|
||
if (!MOUNTMGR_IS_NT_VOLUME_NAME_WB(&volumeName)) {
|
||
ExFreePool(reparse);
|
||
*InvalidBackPointer = TRUE;
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
volumeName.Length -= sizeof(WCHAR);
|
||
|
||
for (l = DeviceInfo->SymbolicLinkNames.Flink;
|
||
l != &DeviceInfo->SymbolicLinkNames; l = l->Flink) {
|
||
|
||
symlinkEntry = CONTAINING_RECORD(l, SYMBOLIC_LINK_NAME_ENTRY,
|
||
ListEntry);
|
||
|
||
if (RtlEqualUnicodeString(&volumeName, &symlinkEntry->SymbolicLinkName,
|
||
TRUE)) {
|
||
|
||
ExFreePool(reparse);
|
||
return STATUS_SUCCESS;
|
||
}
|
||
}
|
||
|
||
ExFreePool(reparse);
|
||
*InvalidBackPointer = TRUE;
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
NTSTATUS
|
||
MountMgrQueryVolumePaths(
|
||
IN PDEVICE_EXTENSION Extension,
|
||
IN PMOUNTED_DEVICE_INFORMATION DeviceInfo,
|
||
IN PLIST_ENTRY DeviceInfoList,
|
||
OUT PMOUNTMGR_VOLUME_PATHS* VolumePaths,
|
||
OUT PMOUNTED_DEVICE_INFORMATION* ReconcileThisDeviceInfo
|
||
)
|
||
|
||
{
|
||
PLIST_ENTRY l;
|
||
PMOUNTMGR_DEVICE_ENTRY entry;
|
||
ULONG MultiSzLength;
|
||
PSYMBOLIC_LINK_NAME_ENTRY symlinkEntry;
|
||
ULONG numPoints, i, j, k;
|
||
PMOUNTMGR_MOUNT_POINT_ENTRY mountPointEntry;
|
||
PMOUNTMGR_VOLUME_PATHS* childVolumePaths;
|
||
NTSTATUS status;
|
||
PMOUNTMGR_VOLUME_PATHS volumePaths;
|
||
LIST_ENTRY deviceInfoList;
|
||
BOOLEAN invalidBackPointer;
|
||
|
||
MultiSzLength = sizeof(WCHAR);
|
||
|
||
for (l = DeviceInfo->SymbolicLinkNames.Flink;
|
||
l != &DeviceInfo->SymbolicLinkNames; l = l->Flink) {
|
||
|
||
symlinkEntry = CONTAINING_RECORD(l, SYMBOLIC_LINK_NAME_ENTRY,
|
||
ListEntry);
|
||
if (MOUNTMGR_IS_DRIVE_LETTER(&symlinkEntry->SymbolicLinkName) &&
|
||
symlinkEntry->IsInDatabase) {
|
||
|
||
MultiSzLength += 3*sizeof(WCHAR);
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (l == &DeviceInfo->SymbolicLinkNames) {
|
||
symlinkEntry = NULL;
|
||
}
|
||
|
||
for (l = DeviceInfoList->Flink; l != DeviceInfoList; l = l->Flink) {
|
||
|
||
entry = CONTAINING_RECORD(l, MOUNTMGR_DEVICE_ENTRY, ListEntry);
|
||
|
||
if (entry->DeviceInfo == DeviceInfo) {
|
||
volumePaths = ExAllocatePool(PagedPool,
|
||
FIELD_OFFSET(MOUNTMGR_VOLUME_PATHS, MultiSz) +
|
||
MultiSzLength);
|
||
if (!volumePaths) {
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
volumePaths->MultiSzLength = MultiSzLength;
|
||
if (symlinkEntry) {
|
||
volumePaths->MultiSz[0] =
|
||
symlinkEntry->SymbolicLinkName.Buffer[12];
|
||
volumePaths->MultiSz[1] = ':';
|
||
volumePaths->MultiSz[2] = 0;
|
||
volumePaths->MultiSz[3] = 0;
|
||
} else {
|
||
volumePaths->MultiSz[0] = 0;
|
||
}
|
||
|
||
*VolumePaths = volumePaths;
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
}
|
||
|
||
entry = ExAllocatePool(PagedPool, sizeof(MOUNTMGR_DEVICE_ENTRY));
|
||
if (!entry) {
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
entry->DeviceInfo = DeviceInfo;
|
||
InsertTailList(DeviceInfoList, &entry->ListEntry);
|
||
|
||
numPoints = 0;
|
||
for (l = DeviceInfo->MountPointsPointingHere.Flink;
|
||
l != &DeviceInfo->MountPointsPointingHere; l = l->Flink) {
|
||
|
||
numPoints++;
|
||
}
|
||
|
||
if (numPoints) {
|
||
childVolumePaths = ExAllocatePool(PagedPool,
|
||
numPoints*sizeof(PMOUNTMGR_VOLUME_PATHS));
|
||
if (!childVolumePaths) {
|
||
RemoveEntryList(&entry->ListEntry);
|
||
ExFreePool(entry);
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
} else {
|
||
childVolumePaths = NULL;
|
||
}
|
||
|
||
i = 0;
|
||
for (l = DeviceInfo->MountPointsPointingHere.Flink;
|
||
l != &DeviceInfo->MountPointsPointingHere; l = l->Flink, i++) {
|
||
|
||
mountPointEntry = CONTAINING_RECORD(l, MOUNTMGR_MOUNT_POINT_ENTRY,
|
||
ListEntry);
|
||
|
||
invalidBackPointer = FALSE;
|
||
status = MountMgrValidateBackPointer(mountPointEntry, DeviceInfo,
|
||
&invalidBackPointer);
|
||
if (invalidBackPointer) {
|
||
*ReconcileThisDeviceInfo = mountPointEntry->DeviceInfo;
|
||
status = STATUS_UNSUCCESSFUL;
|
||
}
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
for (j = 0; j < i; j++) {
|
||
ExFreePool(childVolumePaths[j]);
|
||
}
|
||
ExFreePool(childVolumePaths);
|
||
RemoveEntryList(&entry->ListEntry);
|
||
ExFreePool(entry);
|
||
return status;
|
||
}
|
||
|
||
status = MountMgrQueryVolumePaths(Extension,
|
||
mountPointEntry->DeviceInfo,
|
||
DeviceInfoList,
|
||
&childVolumePaths[i],
|
||
ReconcileThisDeviceInfo);
|
||
if (!NT_SUCCESS(status)) {
|
||
for (j = 0; j < i; j++) {
|
||
ExFreePool(childVolumePaths[j]);
|
||
}
|
||
ExFreePool(childVolumePaths);
|
||
RemoveEntryList(&entry->ListEntry);
|
||
ExFreePool(entry);
|
||
return status;
|
||
}
|
||
|
||
k = 0;
|
||
for (j = 0; j < childVolumePaths[i]->MultiSzLength/sizeof(WCHAR) - 1;
|
||
j++) {
|
||
|
||
if (!childVolumePaths[i]->MultiSz[j]) {
|
||
k++;
|
||
}
|
||
}
|
||
|
||
MultiSzLength += k*mountPointEntry->MountPath.Length +
|
||
childVolumePaths[i]->MultiSzLength - sizeof(WCHAR);
|
||
}
|
||
|
||
volumePaths = ExAllocatePool(PagedPool,
|
||
FIELD_OFFSET(MOUNTMGR_VOLUME_PATHS, MultiSz) +
|
||
MultiSzLength);
|
||
if (!volumePaths) {
|
||
for (i = 0; i < numPoints; i++) {
|
||
ExFreePool(childVolumePaths[i]);
|
||
}
|
||
if (childVolumePaths) {
|
||
ExFreePool(childVolumePaths);
|
||
}
|
||
RemoveEntryList(&entry->ListEntry);
|
||
ExFreePool(entry);
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
volumePaths->MultiSzLength = MultiSzLength;
|
||
|
||
j = 0;
|
||
if (symlinkEntry) {
|
||
volumePaths->MultiSz[j++] = symlinkEntry->SymbolicLinkName.Buffer[12];
|
||
volumePaths->MultiSz[j++] = ':';
|
||
volumePaths->MultiSz[j++] = 0;
|
||
}
|
||
|
||
i = 0;
|
||
for (l = DeviceInfo->MountPointsPointingHere.Flink;
|
||
l != &DeviceInfo->MountPointsPointingHere; l = l->Flink, i++) {
|
||
|
||
mountPointEntry = CONTAINING_RECORD(l, MOUNTMGR_MOUNT_POINT_ENTRY,
|
||
ListEntry);
|
||
|
||
for (k = 0; k < childVolumePaths[i]->MultiSzLength/sizeof(WCHAR) - 1;
|
||
k++) {
|
||
|
||
if (childVolumePaths[i]->MultiSz[k]) {
|
||
volumePaths->MultiSz[j++] = childVolumePaths[i]->MultiSz[k];
|
||
} else {
|
||
RtlCopyMemory(&volumePaths->MultiSz[j],
|
||
mountPointEntry->MountPath.Buffer,
|
||
mountPointEntry->MountPath.Length);
|
||
j += mountPointEntry->MountPath.Length/sizeof(WCHAR);
|
||
volumePaths->MultiSz[j++] = 0;
|
||
}
|
||
}
|
||
|
||
ExFreePool(childVolumePaths[i]);
|
||
}
|
||
volumePaths->MultiSz[j] = 0;
|
||
|
||
if (childVolumePaths) {
|
||
ExFreePool(childVolumePaths);
|
||
}
|
||
|
||
RemoveEntryList(&entry->ListEntry);
|
||
ExFreePool(entry);
|
||
|
||
*VolumePaths = volumePaths;
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
NTSTATUS
|
||
MountMgrQueryDosVolumePaths(
|
||
IN PDEVICE_EXTENSION Extension,
|
||
IN PIRP Irp
|
||
)
|
||
|
||
{
|
||
PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp);
|
||
PMOUNTMGR_TARGET_NAME input = (PMOUNTMGR_TARGET_NAME) Irp->AssociatedIrp.SystemBuffer;
|
||
PMOUNTMGR_VOLUME_PATHS output = (PMOUNTMGR_VOLUME_PATHS) Irp->AssociatedIrp.SystemBuffer;
|
||
ULONG len, i;
|
||
UNICODE_STRING deviceName;
|
||
NTSTATUS status;
|
||
PMOUNTED_DEVICE_INFORMATION deviceInfo, reconcileThisDeviceInfo, d;
|
||
PMOUNTMGR_VOLUME_PATHS volumePaths;
|
||
LIST_ENTRY deviceInfoList;
|
||
RECONCILE_WORK_ITEM_INFO workItemInfo;
|
||
PLIST_ENTRY l;
|
||
BOOLEAN assertNameChange;
|
||
|
||
if (irpSp->Parameters.DeviceIoControl.InputBufferLength <
|
||
sizeof(MOUNTMGR_TARGET_NAME)) {
|
||
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
if (input->DeviceNameLength&1) {
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
len = FIELD_OFFSET(MOUNTMGR_TARGET_NAME, DeviceName) +
|
||
input->DeviceNameLength;
|
||
if (irpSp->Parameters.DeviceIoControl.InputBufferLength < len) {
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
if (irpSp->Parameters.DeviceIoControl.OutputBufferLength <
|
||
FIELD_OFFSET(MOUNTMGR_VOLUME_PATHS, MultiSz)) {
|
||
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
deviceName.MaximumLength = deviceName.Length = input->DeviceNameLength;
|
||
deviceName.Buffer = input->DeviceName;
|
||
|
||
status = FindDeviceInfo(Extension, &deviceName, FALSE, &deviceInfo);
|
||
if (!NT_SUCCESS(status)) {
|
||
return status;
|
||
}
|
||
|
||
assertNameChange = FALSE;
|
||
for (i = 0; i < 1000; i++) {
|
||
InitializeListHead(&deviceInfoList);
|
||
reconcileThisDeviceInfo = NULL;
|
||
status = MountMgrQueryVolumePaths(Extension, deviceInfo,
|
||
&deviceInfoList, &volumePaths,
|
||
&reconcileThisDeviceInfo);
|
||
if (NT_SUCCESS(status)) {
|
||
break;
|
||
}
|
||
|
||
if (!reconcileThisDeviceInfo) {
|
||
return status;
|
||
}
|
||
|
||
if (!deviceInfo->NotAPdo) {
|
||
assertNameChange = TRUE;
|
||
}
|
||
|
||
workItemInfo.Extension = Extension;
|
||
workItemInfo.DeviceInfo = reconcileThisDeviceInfo;
|
||
KeReleaseSemaphore(&Extension->Mutex, IO_NO_INCREMENT, 1, FALSE);
|
||
|
||
ReconcileThisDatabaseWithMasterWorker(&workItemInfo);
|
||
|
||
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 (d == deviceInfo) {
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (l == &Extension->MountedDeviceList) {
|
||
return STATUS_OBJECT_NAME_NOT_FOUND;
|
||
}
|
||
}
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
return status;
|
||
}
|
||
|
||
if (assertNameChange) {
|
||
MountMgrNotifyNameChange(Extension, &deviceName, FALSE);
|
||
}
|
||
|
||
output->MultiSzLength = volumePaths->MultiSzLength;
|
||
Irp->IoStatus.Information = FIELD_OFFSET(MOUNTMGR_VOLUME_PATHS, MultiSz) +
|
||
output->MultiSzLength;
|
||
|
||
if (Irp->IoStatus.Information >
|
||
irpSp->Parameters.DeviceIoControl.OutputBufferLength) {
|
||
|
||
ExFreePool(volumePaths);
|
||
Irp->IoStatus.Information = FIELD_OFFSET(MOUNTMGR_VOLUME_PATHS,
|
||
MultiSz);
|
||
return STATUS_BUFFER_OVERFLOW;
|
||
}
|
||
|
||
RtlCopyMemory(output->MultiSz, volumePaths->MultiSz,
|
||
output->MultiSzLength);
|
||
|
||
ExFreePool(volumePaths);
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
NTSTATUS
|
||
MountMgrQueryDosVolumePath(
|
||
IN PDEVICE_EXTENSION Extension,
|
||
IN PIRP Irp
|
||
)
|
||
|
||
{
|
||
PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp);
|
||
PMOUNTMGR_TARGET_NAME input = (PMOUNTMGR_TARGET_NAME) Irp->AssociatedIrp.SystemBuffer;
|
||
PMOUNTMGR_VOLUME_PATHS output = (PMOUNTMGR_VOLUME_PATHS) Irp->AssociatedIrp.SystemBuffer;
|
||
ULONG len, i;
|
||
UNICODE_STRING deviceName;
|
||
NTSTATUS status;
|
||
PMOUNTED_DEVICE_INFORMATION deviceInfo, origDeviceInfo;
|
||
PLIST_ENTRY l;
|
||
PSYMBOLIC_LINK_NAME_ENTRY symlinkEntry;
|
||
UNICODE_STRING path, oldPath;
|
||
PMOUNTMGR_MOUNT_POINT_ENTRY mountPointEntry;
|
||
|
||
if (irpSp->Parameters.DeviceIoControl.InputBufferLength <
|
||
sizeof(MOUNTMGR_TARGET_NAME)) {
|
||
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
if (input->DeviceNameLength&1) {
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
len = FIELD_OFFSET(MOUNTMGR_TARGET_NAME, DeviceName) +
|
||
input->DeviceNameLength;
|
||
if (irpSp->Parameters.DeviceIoControl.InputBufferLength < len) {
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
if (irpSp->Parameters.DeviceIoControl.OutputBufferLength <
|
||
FIELD_OFFSET(MOUNTMGR_VOLUME_PATHS, MultiSz)) {
|
||
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
deviceName.MaximumLength = deviceName.Length = input->DeviceNameLength;
|
||
deviceName.Buffer = input->DeviceName;
|
||
|
||
status = FindDeviceInfo(Extension, &deviceName, FALSE, &deviceInfo);
|
||
if (!NT_SUCCESS(status)) {
|
||
return status;
|
||
}
|
||
|
||
origDeviceInfo = deviceInfo;
|
||
|
||
path.Length = path.MaximumLength = 0;
|
||
path.Buffer = NULL;
|
||
|
||
for (i = 0; i < 1000; i++) {
|
||
|
||
for (l = deviceInfo->SymbolicLinkNames.Flink;
|
||
l != &deviceInfo->SymbolicLinkNames; l = l->Flink) {
|
||
|
||
symlinkEntry = CONTAINING_RECORD(l, SYMBOLIC_LINK_NAME_ENTRY,
|
||
ListEntry);
|
||
if (MOUNTMGR_IS_DRIVE_LETTER(&symlinkEntry->SymbolicLinkName) &&
|
||
symlinkEntry->IsInDatabase) {
|
||
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (l != &deviceInfo->SymbolicLinkNames) {
|
||
oldPath = path;
|
||
path.Length += 2*sizeof(WCHAR);
|
||
path.MaximumLength = path.Length;
|
||
path.Buffer = ExAllocatePool(PagedPool, path.MaximumLength);
|
||
if (!path.Buffer) {
|
||
if (oldPath.Buffer) {
|
||
ExFreePool(oldPath.Buffer);
|
||
}
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
path.Buffer[0] = symlinkEntry->SymbolicLinkName.Buffer[12];
|
||
path.Buffer[1] = ':';
|
||
|
||
if (oldPath.Buffer) {
|
||
RtlCopyMemory(&path.Buffer[2], oldPath.Buffer, oldPath.Length);
|
||
ExFreePool(oldPath.Buffer);
|
||
}
|
||
break;
|
||
}
|
||
|
||
if (IsListEmpty(&deviceInfo->MountPointsPointingHere)) {
|
||
break;
|
||
}
|
||
|
||
l = deviceInfo->MountPointsPointingHere.Flink;
|
||
mountPointEntry = CONTAINING_RECORD(l, MOUNTMGR_MOUNT_POINT_ENTRY,
|
||
ListEntry);
|
||
|
||
oldPath = path;
|
||
path.Length += mountPointEntry->MountPath.Length;
|
||
path.MaximumLength = path.Length;
|
||
path.Buffer = ExAllocatePool(PagedPool, path.MaximumLength);
|
||
if (!path.Buffer) {
|
||
if (oldPath.Buffer) {
|
||
ExFreePool(oldPath.Buffer);
|
||
}
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
RtlCopyMemory(path.Buffer, mountPointEntry->MountPath.Buffer,
|
||
mountPointEntry->MountPath.Length);
|
||
|
||
if (oldPath.Buffer) {
|
||
RtlCopyMemory(
|
||
&path.Buffer[mountPointEntry->MountPath.Length/sizeof(WCHAR)],
|
||
oldPath.Buffer, oldPath.Length);
|
||
ExFreePool(oldPath.Buffer);
|
||
}
|
||
|
||
deviceInfo = mountPointEntry->DeviceInfo;
|
||
}
|
||
|
||
if (path.Length < 2*sizeof(WCHAR) || path.Buffer[1] != ':') {
|
||
|
||
if (path.Buffer) {
|
||
ExFreePool(path.Buffer);
|
||
}
|
||
|
||
deviceInfo = origDeviceInfo;
|
||
|
||
for (l = deviceInfo->SymbolicLinkNames.Flink;
|
||
l != &deviceInfo->SymbolicLinkNames; l = l->Flink) {
|
||
|
||
symlinkEntry = CONTAINING_RECORD(l, SYMBOLIC_LINK_NAME_ENTRY,
|
||
ListEntry);
|
||
if (MOUNTMGR_IS_VOLUME_NAME(&symlinkEntry->SymbolicLinkName)) {
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (l != &deviceInfo->SymbolicLinkNames) {
|
||
path.Length = path.MaximumLength =
|
||
symlinkEntry->SymbolicLinkName.Length;
|
||
path.Buffer = ExAllocatePool(PagedPool, path.MaximumLength);
|
||
if (!path.Buffer) {
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
RtlCopyMemory(path.Buffer, symlinkEntry->SymbolicLinkName.Buffer,
|
||
path.Length);
|
||
path.Buffer[1] = '\\';
|
||
}
|
||
}
|
||
|
||
output->MultiSzLength = path.Length + 2*sizeof(WCHAR);
|
||
Irp->IoStatus.Information = FIELD_OFFSET(MOUNTMGR_VOLUME_PATHS, MultiSz) +
|
||
output->MultiSzLength;
|
||
if (Irp->IoStatus.Information >
|
||
irpSp->Parameters.DeviceIoControl.OutputBufferLength) {
|
||
|
||
ExFreePool(path.Buffer);
|
||
Irp->IoStatus.Information = FIELD_OFFSET(MOUNTMGR_VOLUME_PATHS,
|
||
MultiSz);
|
||
return STATUS_BUFFER_OVERFLOW;
|
||
}
|
||
|
||
if (path.Length) {
|
||
RtlCopyMemory(output->MultiSz, path.Buffer, path.Length);
|
||
}
|
||
|
||
if (path.Buffer) {
|
||
ExFreePool(path.Buffer);
|
||
}
|
||
|
||
output->MultiSz[path.Length/sizeof(WCHAR)] = 0;
|
||
output->MultiSz[path.Length/sizeof(WCHAR) + 1] = 0;
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
NTSTATUS
|
||
MountmgrWriteNoAutoMount(
|
||
IN PDEVICE_EXTENSION Extension
|
||
)
|
||
|
||
{
|
||
ULONG NoAutoMount = !Extension->AutoMountPermitted;
|
||
|
||
return RtlWriteRegistryValue(RTL_REGISTRY_ABSOLUTE,
|
||
Extension->RegistryPath.Buffer,
|
||
L"NoAutoMount", REG_DWORD,
|
||
&NoAutoMount, sizeof(NoAutoMount));
|
||
}
|
||
|
||
NTSTATUS
|
||
ScrubRegistryRoutine(
|
||
IN PWSTR ValueName,
|
||
IN ULONG ValueType,
|
||
IN PVOID ValueData,
|
||
IN ULONG ValueLength,
|
||
IN PVOID Extension,
|
||
IN PVOID EntriesDeleted
|
||
)
|
||
|
||
{
|
||
PDEVICE_EXTENSION extension = Extension;
|
||
PBOOLEAN entriesDeleted = EntriesDeleted;
|
||
PLIST_ENTRY l;
|
||
PMOUNTED_DEVICE_INFORMATION deviceInfo;
|
||
NTSTATUS status;
|
||
|
||
if (ValueType != REG_BINARY) {
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
for (l = extension->MountedDeviceList.Flink;
|
||
l != &extension->MountedDeviceList; l = l->Flink) {
|
||
|
||
deviceInfo = CONTAINING_RECORD(l, MOUNTED_DEVICE_INFORMATION,
|
||
ListEntry);
|
||
|
||
if (deviceInfo->UniqueId &&
|
||
deviceInfo->UniqueId->UniqueIdLength == ValueLength &&
|
||
RtlCompareMemory(deviceInfo->UniqueId->UniqueId,
|
||
ValueData, ValueLength) == ValueLength) {
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
}
|
||
|
||
status = RtlDeleteRegistryValue(RTL_REGISTRY_ABSOLUTE,
|
||
MOUNTED_DEVICES_KEY, ValueName);
|
||
if (!NT_SUCCESS(status)) {
|
||
*entriesDeleted = FALSE;
|
||
return status;
|
||
}
|
||
|
||
*entriesDeleted = TRUE;
|
||
|
||
return STATUS_UNSUCCESSFUL;
|
||
}
|
||
|
||
NTSTATUS
|
||
MountMgrScrubRegistry(
|
||
IN PDEVICE_EXTENSION Extension
|
||
)
|
||
|
||
{
|
||
RTL_QUERY_REGISTRY_TABLE queryTable[2];
|
||
BOOLEAN entriesDeleted;
|
||
NTSTATUS status;
|
||
|
||
for (;;) {
|
||
|
||
RtlZeroMemory(queryTable, 2*sizeof(RTL_QUERY_REGISTRY_TABLE));
|
||
queryTable[0].QueryRoutine = ScrubRegistryRoutine;
|
||
queryTable[0].EntryContext = &entriesDeleted;
|
||
entriesDeleted = FALSE;
|
||
|
||
status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
|
||
MOUNTED_DEVICES_KEY, queryTable,
|
||
Extension, NULL);
|
||
if (!entriesDeleted) {
|
||
break;
|
||
}
|
||
}
|
||
|
||
return status;
|
||
}
|
||
|
||
NTSTATUS
|
||
MountMgrQueryAutoMount(
|
||
IN PDEVICE_EXTENSION Extension,
|
||
IN PIRP Irp
|
||
)
|
||
|
||
{
|
||
NTSTATUS status = STATUS_SUCCESS;
|
||
PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp);
|
||
PMOUNTMGR_QUERY_AUTO_MOUNT output = (PMOUNTMGR_QUERY_AUTO_MOUNT) Irp->AssociatedIrp.SystemBuffer;
|
||
|
||
|
||
if (irpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof (MOUNTMGR_QUERY_AUTO_MOUNT)) {
|
||
status = STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
|
||
if (NT_SUCCESS (status)) {
|
||
output->CurrentState = (Extension->AutoMountPermitted) ? Enabled : Disabled;
|
||
Irp->IoStatus.Information = sizeof (MOUNTMGR_QUERY_AUTO_MOUNT);
|
||
} else {
|
||
Irp->IoStatus.Information = 0;
|
||
}
|
||
|
||
|
||
return (status);
|
||
}
|
||
|
||
NTSTATUS
|
||
MountMgrSetAutoMount(
|
||
IN PDEVICE_EXTENSION Extension,
|
||
IN PIRP Irp
|
||
)
|
||
|
||
{
|
||
NTSTATUS status = STATUS_SUCCESS;
|
||
PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp);
|
||
PMOUNTMGR_SET_AUTO_MOUNT input = (PMOUNTMGR_SET_AUTO_MOUNT) Irp->AssociatedIrp.SystemBuffer;
|
||
|
||
|
||
if (irpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(MOUNTMGR_QUERY_AUTO_MOUNT)) {
|
||
status = STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
|
||
if (NT_SUCCESS (status) && ((Enabled == input->NewState) != Extension->AutoMountPermitted)) {
|
||
//
|
||
// Only write to the registry if we are actually changing
|
||
// the state, otherwise just return.
|
||
//
|
||
Extension->AutoMountPermitted = (Enabled == input->NewState);
|
||
status = MountmgrWriteNoAutoMount (Extension);
|
||
}
|
||
|
||
|
||
Irp->IoStatus.Information = 0;
|
||
|
||
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.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS
|
||
|
||
--*/
|
||
|
||
{
|
||
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;
|
||
MountMgrAssignDriveLetters(extension);
|
||
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;
|
||
|
||
case IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATH:
|
||
status = MountMgrQueryDosVolumePath(extension, Irp);
|
||
break;
|
||
|
||
case IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATHS:
|
||
status = MountMgrQueryDosVolumePaths(extension, Irp);
|
||
break;
|
||
|
||
case IOCTL_MOUNTMGR_SCRUB_REGISTRY:
|
||
status = MountMgrScrubRegistry(extension);
|
||
break;
|
||
|
||
case IOCTL_MOUNTMGR_QUERY_AUTO_MOUNT:
|
||
status = MountMgrQueryAutoMount(extension, Irp);
|
||
break;
|
||
|
||
case IOCTL_MOUNTMGR_SET_AUTO_MOUNT:
|
||
status = MountMgrSetAutoMount(extension, Irp);
|
||
break;
|
||
|
||
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 PDEVICE_OBJECT DeviceObject,
|
||
IN PVOID Extension
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This is a worker thread to process work queue items.
|
||
|
||
Arguments:
|
||
|
||
Extension - Supplies the device extension.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PDEVICE_EXTENSION extension = Extension;
|
||
OBJECT_ATTRIBUTES oa;
|
||
KEVENT event;
|
||
LARGE_INTEGER timeout;
|
||
ULONG i;
|
||
NTSTATUS status;
|
||
HANDLE volumeSafeEvent;
|
||
KIRQL irql;
|
||
PLIST_ENTRY l;
|
||
PRECONCILE_WORK_ITEM queueItem;
|
||
|
||
InitializeObjectAttributes(&oa, &VolumeSafeEventName,
|
||
OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, NULL, NULL);
|
||
KeInitializeEvent(&event, NotificationEvent, FALSE);
|
||
timeout.QuadPart = -10*1000*1000; // 1 second
|
||
|
||
for (i = 0; i < 1000; i++) {
|
||
if (Unloading) {
|
||
i = 999;
|
||
continue;
|
||
}
|
||
|
||
status = ZwOpenEvent(&volumeSafeEvent, EVENT_ALL_ACCESS, &oa);
|
||
if (NT_SUCCESS(status)) {
|
||
break;
|
||
}
|
||
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, &timeout);
|
||
}
|
||
|
||
if (i < 1000) {
|
||
for (;;) {
|
||
status = ZwWaitForSingleObject(volumeSafeEvent, FALSE, &timeout);
|
||
if (status != STATUS_TIMEOUT || Unloading) {
|
||
break;
|
||
}
|
||
}
|
||
ZwClose(volumeSafeEvent);
|
||
}
|
||
|
||
for (;;) {
|
||
|
||
KeWaitForSingleObject(&extension->WorkerSemaphore,
|
||
Executive, KernelMode, FALSE, NULL);
|
||
|
||
KeAcquireSpinLock(&extension->WorkerSpinLock, &irql);
|
||
if (IsListEmpty(&extension->WorkerQueue)) {
|
||
KeReleaseSpinLock(&extension->WorkerSpinLock, irql);
|
||
InterlockedDecrement(&extension->WorkerRefCount);
|
||
KeSetEvent(&UnloadEvent, 0, FALSE);
|
||
break;
|
||
}
|
||
l = RemoveHeadList(&extension->WorkerQueue);
|
||
KeReleaseSpinLock(&extension->WorkerSpinLock, irql);
|
||
|
||
queueItem = CONTAINING_RECORD(l, RECONCILE_WORK_ITEM, List);
|
||
queueItem->WorkerRoutine(queueItem->Parameter);
|
||
IoFreeWorkItem(queueItem->WorkItem);
|
||
ExFreePool(queueItem);
|
||
if (InterlockedDecrement(&extension->WorkerRefCount) < 0) {
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
NTSTATUS
|
||
QueueWorkItem(
|
||
IN PDEVICE_EXTENSION Extension,
|
||
IN PRECONCILE_WORK_ITEM WorkItem,
|
||
IN PVOID Parameter
|
||
)
|
||
|
||
/*++
|
||
|
||
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.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS
|
||
|
||
--*/
|
||
|
||
{
|
||
OBJECT_ATTRIBUTES oa;
|
||
NTSTATUS status;
|
||
HANDLE handle;
|
||
KIRQL irql;
|
||
|
||
WorkItem->Parameter = Parameter;
|
||
if (!InterlockedIncrement(&Extension->WorkerRefCount)) {
|
||
IoQueueWorkItem(WorkItem->WorkItem, WorkerThread, DelayedWorkQueue,
|
||
Extension);
|
||
}
|
||
|
||
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.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
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.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
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;
|
||
|
||
IoQueueWorkItem(workItem->WorkItem, UniqueIdChangeNotifyWorker, DelayedWorkQueue, workItem);
|
||
|
||
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
|
||
MountMgrShutdown(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp
|
||
)
|
||
|
||
{
|
||
PDEVICE_EXTENSION extension = DeviceObject->DeviceExtension;
|
||
|
||
InterlockedExchange(&Unloading, TRUE);
|
||
KeInitializeEvent(&UnloadEvent, NotificationEvent, FALSE);
|
||
if (InterlockedIncrement(&extension->WorkerRefCount) > 0) {
|
||
KeReleaseSemaphore(&extension->WorkerSemaphore, 0, 1, FALSE);
|
||
KeWaitForSingleObject(&UnloadEvent, Executive, KernelMode, FALSE,
|
||
NULL);
|
||
} else {
|
||
InterlockedDecrement(&extension->WorkerRefCount);
|
||
}
|
||
|
||
Irp->IoStatus.Status = STATUS_SUCCESS;
|
||
Irp->IoStatus.Information = 0;
|
||
|
||
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
ULONG
|
||
MountmgrReadNoAutoMount(
|
||
IN PUNICODE_STRING RegistryPath
|
||
)
|
||
|
||
{
|
||
ULONG zero, r;
|
||
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 = L"NoAutoMount";
|
||
queryTable[0].EntryContext = &r;
|
||
queryTable[0].DefaultType = REG_DWORD;
|
||
queryTable[0].DefaultData = &zero;
|
||
queryTable[0].DefaultLength = sizeof(ULONG);
|
||
|
||
status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
|
||
RegistryPath->Buffer, queryTable, NULL,
|
||
NULL);
|
||
if (!NT_SUCCESS(status)) {
|
||
r = zero;
|
||
}
|
||
|
||
return r;
|
||
}
|
||
|
||
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
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS status;
|
||
PDEVICE_OBJECT deviceObject;
|
||
PDEVICE_EXTENSION extension;
|
||
|
||
RtlCreateRegistryKey(RTL_REGISTRY_ABSOLUTE, MOUNTED_DEVICES_KEY);
|
||
|
||
status = IoCreateDevice(DriverObject, sizeof(DEVICE_EXTENSION),
|
||
&DeviceName, FILE_DEVICE_NETWORK,
|
||
FILE_DEVICE_SECURE_OPEN, FALSE, &deviceObject);
|
||
if (!NT_SUCCESS(status)) {
|
||
return status;
|
||
}
|
||
|
||
DriverObject->DriverUnload = MountMgrUnload;
|
||
|
||
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);
|
||
InitializeListHead(&extension->UniqueIdChangeNotifyList);
|
||
|
||
extension->RegistryPath.Length = RegistryPath->Length;
|
||
extension->RegistryPath.MaximumLength = extension->RegistryPath.Length +
|
||
sizeof(WCHAR);
|
||
extension->RegistryPath.Buffer = ExAllocatePool(PagedPool,
|
||
extension->RegistryPath.MaximumLength);
|
||
if (!extension->RegistryPath.Buffer) {
|
||
IoDeleteDevice(deviceObject);
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
RtlCopyMemory(extension->RegistryPath.Buffer, RegistryPath->Buffer,
|
||
RegistryPath->Length);
|
||
extension->RegistryPath.Buffer[RegistryPath->Length/sizeof(WCHAR)] = 0;
|
||
extension->AutoMountPermitted = !MountmgrReadNoAutoMount(&extension->RegistryPath);
|
||
|
||
GlobalCreateSymbolicLink(&DeviceSymbolicLinkName, &DeviceName);
|
||
|
||
status = IoRegisterPlugPlayNotification(
|
||
EventCategoryDeviceInterfaceChange,
|
||
PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES,
|
||
(PVOID) &MOUNTDEV_MOUNTED_DEVICE_GUID, DriverObject,
|
||
MountMgrMountedDeviceNotification, extension,
|
||
&extension->NotificationEntry);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
IoDeleteDevice(deviceObject);
|
||
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;
|
||
DriverObject->MajorFunction[IRP_MJ_SHUTDOWN] = MountMgrShutdown;
|
||
gdeviceObject = deviceObject;
|
||
|
||
status = IoRegisterShutdownNotification(gdeviceObject);
|
||
if (!NT_SUCCESS(status)) {
|
||
IoDeleteDevice(deviceObject);
|
||
return status;
|
||
}
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
VOID
|
||
MountMgrUnload(
|
||
PDRIVER_OBJECT DriverObject
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Driver unload routine.
|
||
|
||
Arguments:
|
||
|
||
DeviceObject - Supplies the driver object.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
{
|
||
PDEVICE_EXTENSION extension;
|
||
UNICODE_STRING symbolicLinkName;
|
||
PLIST_ENTRY l;
|
||
PMOUNTED_DEVICE_INFORMATION deviceInfo;
|
||
PSAVED_LINKS_INFORMATION savedLinks;
|
||
PCHANGE_NOTIFY_WORK_ITEM WorkItem;
|
||
|
||
IoUnregisterShutdownNotification(gdeviceObject);
|
||
|
||
extension = gdeviceObject->DeviceExtension;
|
||
|
||
if (extension->RegistryPath.Buffer) {
|
||
ExFreePool(extension->RegistryPath.Buffer);
|
||
extension->RegistryPath.Buffer = NULL;
|
||
}
|
||
|
||
//
|
||
// See if the worker is active
|
||
//
|
||
InterlockedExchange(&Unloading, TRUE);
|
||
KeInitializeEvent (&UnloadEvent, NotificationEvent, FALSE);
|
||
if (InterlockedIncrement(&extension->WorkerRefCount) > 0) {
|
||
KeReleaseSemaphore(&extension->WorkerSemaphore, 0, 1, FALSE);
|
||
KeWaitForSingleObject(&UnloadEvent, Executive, KernelMode, FALSE,
|
||
NULL);
|
||
} else {
|
||
InterlockedDecrement(&extension->WorkerRefCount);
|
||
}
|
||
|
||
IoUnregisterPlugPlayNotification(extension->NotificationEntry);
|
||
|
||
KeWaitForSingleObject(&extension->Mutex,
|
||
Executive,
|
||
KernelMode,
|
||
FALSE,
|
||
NULL);
|
||
|
||
while (!IsListEmpty (&extension->DeadMountedDeviceList)) {
|
||
|
||
l = RemoveHeadList (&extension->DeadMountedDeviceList);
|
||
deviceInfo = CONTAINING_RECORD(l, MOUNTED_DEVICE_INFORMATION, ListEntry);
|
||
|
||
MountMgrFreeDeadDeviceInfo (deviceInfo);
|
||
}
|
||
|
||
while (!IsListEmpty (&extension->MountedDeviceList)) {
|
||
|
||
l = RemoveHeadList (&extension->MountedDeviceList);
|
||
deviceInfo = CONTAINING_RECORD(l, MOUNTED_DEVICE_INFORMATION, ListEntry);
|
||
|
||
MountMgrFreeMountedDeviceInfo (deviceInfo);
|
||
}
|
||
|
||
while (!IsListEmpty (&extension->SavedLinksList)) {
|
||
|
||
l = RemoveHeadList (&extension->SavedLinksList);
|
||
savedLinks = CONTAINING_RECORD(l, SAVED_LINKS_INFORMATION, ListEntry);
|
||
|
||
MountMgrFreeSavedLink (savedLinks);
|
||
}
|
||
|
||
while (!IsListEmpty (&extension->UniqueIdChangeNotifyList)) {
|
||
l = RemoveHeadList (&extension->UniqueIdChangeNotifyList);
|
||
WorkItem = CONTAINING_RECORD(l, CHANGE_NOTIFY_WORK_ITEM, List);
|
||
KeResetEvent (&UnloadEvent);
|
||
|
||
InterlockedExchangePointer (&WorkItem->Event, &UnloadEvent);
|
||
KeReleaseSemaphore(&extension->Mutex, IO_NO_INCREMENT, 1, FALSE);
|
||
|
||
IoCancelIrp (WorkItem->Irp);
|
||
|
||
KeWaitForSingleObject (&UnloadEvent,
|
||
Executive,
|
||
KernelMode,
|
||
FALSE,
|
||
NULL);
|
||
|
||
IoFreeIrp(WorkItem->Irp);
|
||
ExFreePool(WorkItem->DeviceName.Buffer);
|
||
ExFreePool(WorkItem->SystemBuffer);
|
||
ExFreePool(WorkItem);
|
||
KeWaitForSingleObject(&extension->Mutex,
|
||
Executive,
|
||
KernelMode,
|
||
FALSE,
|
||
NULL);
|
||
}
|
||
|
||
if (extension->SystemPartitionUniqueId) {
|
||
ExFreePool(extension->SystemPartitionUniqueId);
|
||
extension->SystemPartitionUniqueId = NULL;
|
||
}
|
||
|
||
KeReleaseSemaphore(&extension->Mutex, IO_NO_INCREMENT, 1, FALSE);
|
||
|
||
|
||
GlobalDeleteSymbolicLink(&DeviceSymbolicLinkName);
|
||
|
||
IoDeleteDevice (gdeviceObject);
|
||
}
|