reactos/drivers/storage/mountmgr/device.c
Hermès Bélusca-Maïto 5f26356079
[MOUNTMGR] Rectify "NoAutoMount" usage (#7030)
This "NoAutoMount" member was not consistently used. Sometimes it was
used correctly, some other times it was used as "not NoAutoMount" i.e.
"AutoMount" enabled.
Fix this consistently throughout the source, and fix also some comments.
2024-06-25 22:51:07 +02:00

2790 lines
90 KiB
C

/*
* ReactOS kernel
* Copyright (C) 2011-2012 ReactOS Team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS kernel
* FILE: drivers/filesystem/mountmgr/device.c
* PURPOSE: Mount Manager - Device Control
* PROGRAMMER: Pierre Schweitzer (pierre.schweitzer@reactos.org)
*/
#include "mntmgr.h"
#define MAX_DEVICES 0x3E8 /* Matches 1000 devices */
#define NDEBUG
#include <debug.h>
/*
* @implemented
*/
NTSTATUS
MountMgrChangeNotify(IN PDEVICE_EXTENSION DeviceExtension,
IN PIRP Irp)
{
KIRQL OldIrql;
NTSTATUS Status;
PIO_STACK_LOCATION Stack;
PMOUNTMGR_CHANGE_NOTIFY_INFO ChangeNotify;
/* Get the I/O buffer */
Stack = IoGetCurrentIrpStackLocation(Irp);
ChangeNotify = (PMOUNTMGR_CHANGE_NOTIFY_INFO)Irp->AssociatedIrp.SystemBuffer;
/* Validate it */
if (Stack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(MOUNTMGR_CHANGE_NOTIFY_INFO) ||
Stack->Parameters.DeviceIoControl.InputBufferLength < sizeof(MOUNTMGR_CHANGE_NOTIFY_INFO))
{
return STATUS_INVALID_PARAMETER;
}
/* If epic number doesn't match, just return now one */
if (DeviceExtension->EpicNumber != ChangeNotify->EpicNumber)
{
ChangeNotify->EpicNumber = DeviceExtension->EpicNumber;
Irp->IoStatus.Information = sizeof(MOUNTMGR_CHANGE_NOTIFY_INFO);
return STATUS_SUCCESS;
}
/* If IRP is to be canceled, forget about that */
IoAcquireCancelSpinLock(&OldIrql);
if (Irp->Cancel)
{
Status = STATUS_CANCELLED;
}
/* Otherwise queue the IRP to be notified with the next epic number change */
else
{
InsertTailList(&(DeviceExtension->IrpListHead), &(Irp->Tail.Overlay.ListEntry));
IoMarkIrpPending(Irp);
IoSetCancelRoutine(Irp, MountMgrCancel);
Status = STATUS_PENDING;
}
IoReleaseCancelSpinLock(OldIrql);
return Status;
}
/*
* @implemented
*/
NTSTATUS
MountmgrWriteNoAutoMount(IN PDEVICE_EXTENSION DeviceExtension)
{
ULONG Value = DeviceExtension->NoAutoMount;
return RtlWriteRegistryValue(RTL_REGISTRY_ABSOLUTE,
DeviceExtension->RegistryPath.Buffer,
L"NoAutoMount",
REG_DWORD,
&Value,
sizeof(Value));
}
/*
* @implemented
*/
NTSTATUS
MountMgrSetAutoMount(IN PDEVICE_EXTENSION DeviceExtension,
IN PIRP Irp)
{
PIO_STACK_LOCATION Stack;
PMOUNTMGR_SET_AUTO_MOUNT SetState;
Stack = IoGetCurrentIrpStackLocation(Irp);
if (Stack->Parameters.DeviceIoControl.InputBufferLength < sizeof(MOUNTMGR_SET_AUTO_MOUNT))
{
Irp->IoStatus.Information = 0;
return STATUS_INVALID_PARAMETER;
}
/* Change the state only if there is a real difference
* with the user-provided NewState (normalized) */
SetState = (PMOUNTMGR_SET_AUTO_MOUNT)Irp->AssociatedIrp.SystemBuffer;
if ((SetState->NewState != Enabled) == DeviceExtension->NoAutoMount)
{
Irp->IoStatus.Information = 0;
return STATUS_SUCCESS;
}
/* Set new state */
DeviceExtension->NoAutoMount = (SetState->NewState != Enabled);
Irp->IoStatus.Information = 0;
return MountmgrWriteNoAutoMount(DeviceExtension);
}
/*
* @implemented
*/
NTSTATUS
MountMgrQueryAutoMount(IN PDEVICE_EXTENSION DeviceExtension,
IN PIRP Irp)
{
PIO_STACK_LOCATION Stack;
PMOUNTMGR_QUERY_AUTO_MOUNT QueryState;
Stack = IoGetCurrentIrpStackLocation(Irp);
if (Stack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(MOUNTMGR_QUERY_AUTO_MOUNT))
{
Irp->IoStatus.Information = 0;
return STATUS_INVALID_PARAMETER;
}
QueryState = (PMOUNTMGR_QUERY_AUTO_MOUNT)Irp->AssociatedIrp.SystemBuffer;
QueryState->CurrentState = !DeviceExtension->NoAutoMount;
Irp->IoStatus.Information = sizeof(MOUNTMGR_QUERY_AUTO_MOUNT);
return STATUS_SUCCESS;
}
/*
* @implemented
*/
NTSTATUS
NTAPI
ScrubRegistryRoutine(IN PWSTR ValueName,
IN ULONG ValueType,
IN PVOID ValueData,
IN ULONG ValueLength,
IN PVOID Context,
IN PVOID EntryContext)
{
NTSTATUS Status;
PLIST_ENTRY NextEntry;
PDEVICE_INFORMATION DeviceInfo;
PBOOLEAN Continue = EntryContext;
PDEVICE_EXTENSION DeviceExtension = Context;
if (ValueType != REG_BINARY)
{
return STATUS_SUCCESS;
}
/* Delete values for devices that don't have the matching unique ID */
if (!IsListEmpty(&(DeviceExtension->DeviceListHead)))
{
for (NextEntry = DeviceExtension->DeviceListHead.Flink;
NextEntry != &(DeviceExtension->DeviceListHead);
NextEntry = NextEntry->Flink)
{
DeviceInfo = CONTAINING_RECORD(NextEntry,
DEVICE_INFORMATION,
DeviceListEntry);
if (!DeviceInfo->UniqueId || DeviceInfo->UniqueId->UniqueIdLength != ValueLength)
{
continue;
}
if (RtlCompareMemory(DeviceInfo->UniqueId->UniqueId, ValueData, ValueLength) == ValueLength)
{
return STATUS_SUCCESS;
}
}
}
/* Wrong unique ID, scrub it */
Status = RtlDeleteRegistryValue(RTL_REGISTRY_ABSOLUTE,
DatabasePath,
ValueName);
if (!NT_SUCCESS(Status))
{
*Continue = TRUE;
return STATUS_UNSUCCESSFUL;
}
*Continue = FALSE;
return Status;
}
/*
* @implemented
*/
NTSTATUS
MountMgrScrubRegistry(IN PDEVICE_EXTENSION DeviceExtension)
{
NTSTATUS Status;
BOOLEAN Continue;
RTL_QUERY_REGISTRY_TABLE QueryTable[2];
do
{
RtlZeroMemory(QueryTable, sizeof(QueryTable));
QueryTable[0].QueryRoutine = ScrubRegistryRoutine;
QueryTable[0].EntryContext = &Continue;
Continue = FALSE;
Status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
DatabasePath,
QueryTable,
DeviceExtension,
NULL);
}
while (Continue);
return Status;
}
/*
* @implemented
*/
NTSTATUS
MountMgrCreatePoint(IN PDEVICE_EXTENSION DeviceExtension,
IN PIRP Irp)
{
ULONG MaxLength;
PIO_STACK_LOCATION Stack;
PMOUNTMGR_CREATE_POINT_INPUT Point;
UNICODE_STRING DeviceName, SymbolicName;
Stack = IoGetCurrentIrpStackLocation(Irp);
if (Stack->Parameters.DeviceIoControl.InputBufferLength < sizeof(MOUNTMGR_CREATE_POINT_INPUT))
{
return STATUS_INVALID_PARAMETER;
}
Point = (PMOUNTMGR_CREATE_POINT_INPUT)Irp->AssociatedIrp.SystemBuffer;
MaxLength = MAX((Point->DeviceNameOffset + Point->DeviceNameLength),
(Point->SymbolicLinkNameLength + Point->SymbolicLinkNameOffset));
if (MaxLength > Stack->Parameters.DeviceIoControl.InputBufferLength)
{
return STATUS_INVALID_PARAMETER;
}
/* Get all the strings and call the worker */
SymbolicName.Length = Point->SymbolicLinkNameLength;
SymbolicName.MaximumLength = Point->SymbolicLinkNameLength;
DeviceName.Length = Point->DeviceNameLength;
DeviceName.MaximumLength = Point->DeviceNameLength;
SymbolicName.Buffer = (PVOID)((ULONG_PTR)Point + Point->SymbolicLinkNameOffset);
DeviceName.Buffer = (PVOID)((ULONG_PTR)Point + Point->DeviceNameOffset);
return MountMgrCreatePointWorker(DeviceExtension, &SymbolicName, &DeviceName);
}
/*
* @implemented
*/
NTSTATUS
MountMgrCheckUnprocessedVolumes(IN PDEVICE_EXTENSION DeviceExtension,
IN PIRP Irp)
{
PLIST_ENTRY NextEntry;
PDEVICE_INFORMATION DeviceInformation;
NTSTATUS ArrivalStatus, Status = STATUS_SUCCESS;
UNREFERENCED_PARAMETER(Irp);
/* No offline volumes, nothing more to do */
if (IsListEmpty(&(DeviceExtension->OfflineDeviceListHead)))
{
KeReleaseSemaphore(&(DeviceExtension->DeviceLock), IO_NO_INCREMENT, 1, FALSE);
return STATUS_SUCCESS;
}
KeReleaseSemaphore(&(DeviceExtension->DeviceLock), IO_NO_INCREMENT, 1, FALSE);
/* Reactivate all the offline volumes */
while (!IsListEmpty(&(DeviceExtension->OfflineDeviceListHead)))
{
NextEntry = RemoveHeadList(&(DeviceExtension->OfflineDeviceListHead));
DeviceInformation = CONTAINING_RECORD(NextEntry, DEVICE_INFORMATION, DeviceListEntry);
ArrivalStatus = MountMgrMountedDeviceArrival(DeviceExtension,
&(DeviceInformation->SymbolicName),
DeviceInformation->ManuallyRegistered);
/* Then, remove them dead information */
MountMgrFreeDeadDeviceInfo(DeviceInformation);
if (NT_SUCCESS(Status))
{
Status = ArrivalStatus;
}
}
return Status;
}
/*
* @implemented
*/
BOOLEAN
IsFtVolume(IN PUNICODE_STRING SymbolicName)
{
NTSTATUS Status;
PFILE_OBJECT FileObject;
PARTITION_INFORMATION PartitionInfo;
PDEVICE_OBJECT DeviceObject, FileDeviceObject;
/* Get device object */
Status = IoGetDeviceObjectPointer(SymbolicName,
FILE_READ_ATTRIBUTES,
&FileObject,
&DeviceObject);
if (!NT_SUCCESS(Status))
return FALSE;
/* Get attached device */
FileDeviceObject = FileObject->DeviceObject;
DeviceObject = IoGetAttachedDeviceReference(FileDeviceObject);
/* FT volume can't be removable */
if (FileDeviceObject->Characteristics & FILE_REMOVABLE_MEDIA)
{
ObDereferenceObject(DeviceObject);
ObDereferenceObject(FileObject);
return FALSE;
}
ObDereferenceObject(FileObject);
/* Get partition information */
Status = MountMgrSendSyncDeviceIoCtl(IOCTL_DISK_GET_PARTITION_INFO,
DeviceObject,
NULL,
0,
&PartitionInfo,
sizeof(PartitionInfo),
NULL);
ObDereferenceObject(DeviceObject);
if (!NT_SUCCESS(Status))
return FALSE;
/* Check if this is a FT volume */
return IsFTPartition(PartitionInfo.PartitionType);
}
/*
* @implemented
*/
VOID
ProcessSuggestedDriveLetters(IN PDEVICE_EXTENSION DeviceExtension)
{
WCHAR NameBuffer[DRIVE_LETTER_LENGTH / sizeof(WCHAR)];
PLIST_ENTRY NextEntry;
UNICODE_STRING SymbolicName;
PDEVICE_INFORMATION DeviceInformation;
/* No devices? Nothing to do! */
if (IsListEmpty(&(DeviceExtension->DeviceListHead)))
{
return;
}
/* For all the devices */
for (NextEntry = DeviceExtension->DeviceListHead.Flink;
NextEntry != &(DeviceExtension->DeviceListHead);
NextEntry = NextEntry->Flink)
{
DeviceInformation = CONTAINING_RECORD(NextEntry, DEVICE_INFORMATION, DeviceListEntry);
/* If no drive letter */
if (DeviceInformation->SuggestedDriveLetter == (UCHAR)-1)
{
/* Ensure it has no entry yet */
if (!HasDriveLetter(DeviceInformation) &&
!HasNoDriveLetterEntry(DeviceInformation->UniqueId))
{
/* And create one */
CreateNoDriveLetterEntry(DeviceInformation->UniqueId);
}
DeviceInformation->SuggestedDriveLetter = 0;
}
/* Suggested letter & no entry */
else if (DeviceInformation->SuggestedDriveLetter &&
!HasNoDriveLetterEntry(DeviceInformation->UniqueId))
{
/* Just create a mount point */
SymbolicName.Buffer = NameBuffer;
RtlCopyMemory(NameBuffer, DosDevices.Buffer, DosDevices.Length);
NameBuffer[LETTER_POSITION] = DeviceInformation->SuggestedDriveLetter;
NameBuffer[COLON_POSITION] = L':';
SymbolicName.Length =
SymbolicName.MaximumLength = DRIVE_LETTER_LENGTH;
MountMgrCreatePointWorker(DeviceExtension, &SymbolicName, &(DeviceInformation->DeviceName));
}
}
}
/*
* @implemented
*/
NTSTATUS
MountMgrNextDriveLetterWorker(IN PDEVICE_EXTENSION DeviceExtension,
IN PUNICODE_STRING DeviceName,
OUT PMOUNTMGR_DRIVE_LETTER_INFORMATION DriveLetterInfo)
{
NTSTATUS Status;
UCHAR DriveLetter;
PLIST_ENTRY NextEntry;
PMOUNTDEV_UNIQUE_ID UniqueId;
BOOLEAN Removable, GptDriveLetter;
PDEVICE_INFORMATION DeviceInformation;
WCHAR NameBuffer[DRIVE_LETTER_LENGTH];
PSYMLINK_INFORMATION SymlinkInformation;
UNICODE_STRING TargetDeviceName, SymbolicName;
/* First, process suggested letters */
if (!DeviceExtension->ProcessedSuggestions)
{
ProcessSuggestedDriveLetters(DeviceExtension);
DeviceExtension->ProcessedSuggestions = TRUE;
}
/* Then, get information about the device */
Status = QueryDeviceInformation(DeviceName, &TargetDeviceName, NULL, &Removable, &GptDriveLetter, NULL, NULL, NULL);
if (!NT_SUCCESS(Status))
{
return Status;
}
/* Ensure we have such device */
NextEntry = DeviceExtension->DeviceListHead.Flink;
while (NextEntry != &(DeviceExtension->DeviceListHead))
{
DeviceInformation = CONTAINING_RECORD(NextEntry, DEVICE_INFORMATION, DeviceListEntry);
if (RtlCompareUnicodeString(&(DeviceInformation->DeviceName), &TargetDeviceName, TRUE) == 0)
{
break;
}
NextEntry = NextEntry->Flink;
}
if (NextEntry == &(DeviceExtension->DeviceListHead))
{
FreePool(TargetDeviceName.Buffer);
return STATUS_OBJECT_NAME_NOT_FOUND;
}
/* Now, assume we will assign a letter */
DeviceInformation->LetterAssigned =
DriveLetterInfo->DriveLetterWasAssigned = TRUE;
/* Browse all the symlinks to check if there is already a drive letter */
NextEntry = DeviceInformation->SymbolicLinksListHead.Flink;
while (NextEntry != &(DeviceInformation->SymbolicLinksListHead))
{
SymlinkInformation = CONTAINING_RECORD(NextEntry, SYMLINK_INFORMATION, SymbolicLinksListEntry);
/* If this is a drive letter and it is online, forget about new drive letter */
if (IsDriveLetter(&(SymlinkInformation->Name)) && SymlinkInformation->Online)
{
DriveLetterInfo->DriveLetterWasAssigned = FALSE;
DriveLetterInfo->CurrentDriveLetter = (CHAR)SymlinkInformation->Name.Buffer[LETTER_POSITION];
break;
}
NextEntry = NextEntry->Flink;
}
/* If we didn't find a drive letter online, ensure this is not
* a no-drive entry by querying GPT attributes & database */
if (NextEntry == &(DeviceInformation->SymbolicLinksListHead))
{
if (!GptDriveLetter || HasNoDriveLetterEntry(DeviceInformation->UniqueId))
{
DriveLetterInfo->DriveLetterWasAssigned = FALSE;
DriveLetterInfo->CurrentDriveLetter = 0;
goto Release;
}
}
/* If automount is disabled, and the device is not removable
* but needs a drive letter, don't assign one and bail out */
if (DeviceExtension->NoAutoMount && !Removable)
{
if (DriveLetterInfo->DriveLetterWasAssigned)
{
DriveLetterInfo->DriveLetterWasAssigned = FALSE;
DriveLetterInfo->CurrentDriveLetter = 0;
goto Release;
}
}
/* Stop now if we don't need to assign the drive a letter */
if (!DriveLetterInfo->DriveLetterWasAssigned)
goto Release;
/* Now everything is fine, begin drive letter assignment */
if (RtlPrefixUnicodeString(&DeviceFloppy, &TargetDeviceName, TRUE))
{
/* If the device is a floppy, start with letter A */
DriveLetter = 'A';
}
else if (RtlPrefixUnicodeString(&DeviceCdRom, &TargetDeviceName, TRUE))
{
/* If the device is a CD-ROM, start with letter D */
DriveLetter = 'D';
}
else
{
/* Finally, if it's a disk, use C */
DriveLetter = 'C';
}
/* We cannot set NO drive letter */
ASSERT(DeviceInformation->SuggestedDriveLetter != (UCHAR)-1);
/* If we don't have suggested letter but it's a FT volume, fail */
if (!DeviceInformation->SuggestedDriveLetter && IsFtVolume(&(DeviceInformation->DeviceName)))
{
DriveLetterInfo->DriveLetterWasAssigned = FALSE;
DriveLetterInfo->CurrentDriveLetter = 0;
goto Release;
}
/* Prepare buffer */
RtlCopyMemory(NameBuffer, DosDevices.Buffer, DosDevices.Length);
NameBuffer[COLON_POSITION] = L':';
SymbolicName.Buffer = NameBuffer;
SymbolicName.Length =
SymbolicName.MaximumLength = DRIVE_LETTER_LENGTH;
/* It's all prepared, create mount point */
if (DeviceInformation->SuggestedDriveLetter)
{
DriveLetterInfo->CurrentDriveLetter = DeviceInformation->SuggestedDriveLetter;
NameBuffer[LETTER_POSITION] = DeviceInformation->SuggestedDriveLetter;
Status = MountMgrCreatePointWorker(DeviceExtension, &SymbolicName, &TargetDeviceName);
if (NT_SUCCESS(Status))
{
goto Release;
}
}
/* It failed with this letter... Try another one! */
for (DriveLetterInfo->CurrentDriveLetter = DriveLetter;
DriveLetterInfo->CurrentDriveLetter <= L'Z';
DriveLetterInfo->CurrentDriveLetter++)
{
NameBuffer[LETTER_POSITION] = DriveLetterInfo->CurrentDriveLetter;
Status = MountMgrCreatePointWorker(DeviceExtension, &SymbolicName, &TargetDeviceName);
if (NT_SUCCESS(Status))
{
break;
}
}
/* We failed setting a letter */
if (DriveLetterInfo->CurrentDriveLetter > L'Z')
{
DriveLetterInfo->DriveLetterWasAssigned = FALSE;
DriveLetterInfo->CurrentDriveLetter = 0;
/* Try at least to add a no drive letter entry */
Status = QueryDeviceInformation(&TargetDeviceName, NULL, &UniqueId, NULL, NULL, NULL, NULL, NULL);
if (NT_SUCCESS(Status))
{
CreateNoDriveLetterEntry(UniqueId);
FreePool(UniqueId);
}
}
Release:
FreePool(TargetDeviceName.Buffer);
return STATUS_SUCCESS;
}
/*
* @implemented
*/
NTSTATUS
MountMgrNextDriveLetter(IN PDEVICE_EXTENSION DeviceExtension,
IN PIRP Irp)
{
NTSTATUS Status;
PIO_STACK_LOCATION Stack;
UNICODE_STRING DeviceName;
PMOUNTMGR_DRIVE_LETTER_TARGET DriveLetterTarget;
MOUNTMGR_DRIVE_LETTER_INFORMATION DriveLetterInformation;
Stack = IoGetCurrentIrpStackLocation(Irp);
/* Validate input */
if (Stack->Parameters.DeviceIoControl.InputBufferLength < sizeof(MOUNTMGR_DRIVE_LETTER_TARGET) ||
Stack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(MOUNTMGR_DRIVE_LETTER_INFORMATION))
{
return STATUS_INVALID_PARAMETER;
}
DriveLetterTarget = (PMOUNTMGR_DRIVE_LETTER_TARGET)Irp->AssociatedIrp.SystemBuffer;
if (DriveLetterTarget->DeviceNameLength + sizeof(USHORT) > Stack->Parameters.DeviceIoControl.InputBufferLength)
{
return STATUS_INVALID_PARAMETER;
}
/* Call the worker */
DeviceName.Buffer = DriveLetterTarget->DeviceName;
DeviceName.Length =
DeviceName.MaximumLength = DriveLetterTarget->DeviceNameLength;
Status = MountMgrNextDriveLetterWorker(DeviceExtension, &DeviceName,
&DriveLetterInformation);
if (NT_SUCCESS(Status))
{
*(PMOUNTMGR_DRIVE_LETTER_INFORMATION)Irp->AssociatedIrp.SystemBuffer =
DriveLetterInformation;
Irp->IoStatus.Information = sizeof(MOUNTMGR_DRIVE_LETTER_INFORMATION);
}
return Status;
}
/*
* @implemented
*/
NTSTATUS
NTAPI
MountMgrQuerySystemVolumeNameQueryRoutine(IN PWSTR ValueName,
IN ULONG ValueType,
IN PVOID ValueData,
IN ULONG ValueLength,
IN PVOID Context,
IN PVOID EntryContext)
{
UNICODE_STRING ValueString;
PUNICODE_STRING SystemVolumeName;
UNREFERENCED_PARAMETER(ValueName);
UNREFERENCED_PARAMETER(ValueLength);
UNREFERENCED_PARAMETER(EntryContext);
if (ValueType != REG_SZ)
{
return STATUS_SUCCESS;
}
RtlInitUnicodeString(&ValueString, ValueData);
SystemVolumeName = Context;
/* Return a string containing system volume name */
SystemVolumeName->Length = ValueString.Length;
SystemVolumeName->MaximumLength = ValueString.Length + sizeof(WCHAR);
SystemVolumeName->Buffer = AllocatePool(SystemVolumeName->MaximumLength);
if (SystemVolumeName->Buffer)
{
RtlCopyMemory(SystemVolumeName->Buffer, ValueData, ValueString.Length);
SystemVolumeName->Buffer[ValueString.Length / sizeof(WCHAR)] = UNICODE_NULL;
}
return STATUS_SUCCESS;
}
/*
* @implemented
*/
NTSTATUS
MountMgrQuerySystemVolumeName(OUT PUNICODE_STRING SystemVolumeName)
{
RTL_QUERY_REGISTRY_TABLE QueryTable[2];
RtlZeroMemory(QueryTable, sizeof(QueryTable));
QueryTable[0].QueryRoutine = MountMgrQuerySystemVolumeNameQueryRoutine;
QueryTable[0].Flags = RTL_QUERY_REGISTRY_REQUIRED;
QueryTable[0].Name = L"SystemPartition";
SystemVolumeName->Buffer = NULL;
RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
L"\\Registry\\Machine\\System\\Setup",
QueryTable,
SystemVolumeName,
NULL);
if (SystemVolumeName->Buffer)
{
return STATUS_SUCCESS;
}
return STATUS_UNSUCCESSFUL;
}
/*
* @implemented
*/
VOID
MountMgrAssignDriveLetters(IN PDEVICE_EXTENSION DeviceExtension)
{
NTSTATUS Status;
PLIST_ENTRY NextEntry;
UNICODE_STRING SystemVolumeName;
PDEVICE_INFORMATION DeviceInformation;
MOUNTMGR_DRIVE_LETTER_INFORMATION DriveLetterInformation;
/* First, get system volume name */
Status = MountMgrQuerySystemVolumeName(&SystemVolumeName);
/* If there are no device, it's all done */
if (IsListEmpty(&(DeviceExtension->DeviceListHead)))
{
if (NT_SUCCESS(Status))
{
FreePool(SystemVolumeName.Buffer);
}
return;
}
/* Now, for all the devices... */
for (NextEntry = DeviceExtension->DeviceListHead.Flink;
NextEntry != &(DeviceExtension->DeviceListHead);
NextEntry = NextEntry->Flink)
{
DeviceInformation = CONTAINING_RECORD(NextEntry, DEVICE_INFORMATION, DeviceListEntry);
/* If the device doesn't have a letter assigned, do it! */
if (!DeviceInformation->LetterAssigned)
{
MountMgrNextDriveLetterWorker(DeviceExtension,
&(DeviceInformation->DeviceName),
&DriveLetterInformation);
}
/* If it's the system volume */
if (NT_SUCCESS(Status) && RtlEqualUnicodeString(&SystemVolumeName, &(DeviceInformation->DeviceName), TRUE))
{
/* Keep track of it */
DeviceExtension->DriveLetterData = AllocatePool(DeviceInformation->UniqueId->UniqueIdLength +
sizeof(MOUNTDEV_UNIQUE_ID));
if (DeviceExtension->DriveLetterData)
{
RtlCopyMemory(DeviceExtension->DriveLetterData,
DeviceInformation->UniqueId,
DeviceInformation->UniqueId->UniqueIdLength + sizeof(MOUNTDEV_UNIQUE_ID));
}
/* Ensure it gets mounted if automount is disabled */
if (DeviceExtension->NoAutoMount)
{
/* Temporarily re-enable automount for the
* worker to mount and set a drive letter */
DeviceExtension->NoAutoMount = FALSE;
MountMgrNextDriveLetterWorker(DeviceExtension,
&(DeviceInformation->DeviceName),
&DriveLetterInformation);
/* And re-disable automount */
DeviceExtension->NoAutoMount = TRUE;
}
}
}
if (NT_SUCCESS(Status))
{
FreePool(SystemVolumeName.Buffer);
}
}
/*
* @implemented
*/
NTSTATUS
MountMgrQueryDosVolumePath(IN PDEVICE_EXTENSION DeviceExtension,
IN PIRP Irp)
{
NTSTATUS Status;
ULONG DevicesFound;
PIO_STACK_LOCATION Stack;
PLIST_ENTRY SymlinksEntry;
UNICODE_STRING SymbolicName;
PMOUNTMGR_TARGET_NAME Target;
PWSTR DeviceString, OldBuffer;
USHORT DeviceLength, OldLength;
PDEVICE_INFORMATION DeviceInformation;
PSYMLINK_INFORMATION SymlinkInformation;
PASSOCIATED_DEVICE_ENTRY AssociatedDevice;
Stack = IoGetCurrentIrpStackLocation(Irp);
/* Validate input size */
if (Stack->Parameters.DeviceIoControl.InputBufferLength < sizeof(MOUNTMGR_TARGET_NAME))
{
return STATUS_INVALID_PARAMETER;
}
/* Ensure we have received UNICODE_STRING */
Target = (PMOUNTMGR_TARGET_NAME)Irp->AssociatedIrp.SystemBuffer;
if (Target->DeviceNameLength & 1)
{
return STATUS_INVALID_PARAMETER;
}
/* Validate the entry structure size */
if ((FIELD_OFFSET(MOUNTMGR_TARGET_NAME, DeviceNameLength) + Target->DeviceNameLength) >
Stack->Parameters.DeviceIoControl.InputBufferLength)
{
return STATUS_INVALID_PARAMETER;
}
/* Ensure we can at least return needed size */
if (Stack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(ULONG))
{
return STATUS_INVALID_PARAMETER;
}
/* Construct string for query */
SymbolicName.Length = Target->DeviceNameLength;
SymbolicName.MaximumLength = Target->DeviceNameLength;
SymbolicName.Buffer = Target->DeviceName;
/* Find device with our info */
Status = FindDeviceInfo(DeviceExtension, &SymbolicName, FALSE, &DeviceInformation);
if (!NT_SUCCESS(Status))
{
return Status;
}
DeviceLength = 0;
DeviceString = NULL;
DevicesFound = 0;
/* Try to find associated device info */
while (TRUE)
{
for (SymlinksEntry = DeviceInformation->SymbolicLinksListHead.Flink;
SymlinksEntry != &(DeviceInformation->SymbolicLinksListHead);
SymlinksEntry = SymlinksEntry->Flink)
{
SymlinkInformation = CONTAINING_RECORD(SymlinksEntry, SYMLINK_INFORMATION, SymbolicLinksListEntry);
/* Try to find with drive letter */
if (MOUNTMGR_IS_DRIVE_LETTER(&SymlinkInformation->Name) && SymlinkInformation->Online)
{
break;
}
}
/* We didn't find, break */
if (SymlinksEntry == &(DeviceInformation->SymbolicLinksListHead))
{
return STATUS_NOT_FOUND;
}
/* It doesn't have associated device, go to fallback method */
if (IsListEmpty(&DeviceInformation->AssociatedDevicesHead))
{
goto TryWithVolumeName;
}
/* Create a string with the information about the device */
AssociatedDevice = CONTAINING_RECORD(&(DeviceInformation->SymbolicLinksListHead), ASSOCIATED_DEVICE_ENTRY, AssociatedDevicesEntry);
OldLength = DeviceLength;
OldBuffer = DeviceString;
DeviceLength += AssociatedDevice->String.Length;
DeviceString = AllocatePool(DeviceLength);
if (!DeviceString)
{
if (OldBuffer)
{
FreePool(OldBuffer);
}
return STATUS_INSUFFICIENT_RESOURCES;
}
/* Store our info and previous if any */
RtlCopyMemory(DeviceString, AssociatedDevice->String.Buffer, AssociatedDevice->String.Length);
if (OldBuffer)
{
RtlCopyMemory(&DeviceString[AssociatedDevice->String.Length / sizeof(WCHAR)], OldBuffer, OldLength);
FreePool(OldBuffer);
}
/* Count and continue looking */
++DevicesFound;
DeviceInformation = AssociatedDevice->DeviceInformation;
/* If too many devices, try another way */
if (DevicesFound > MAX_DEVICES) /* 1000 */
{
goto TryWithVolumeName;
}
}
/* Reallocate our string, so that we can prepend disk letter */
OldBuffer = DeviceString;
OldLength = DeviceLength;
DeviceLength += 2 * sizeof(WCHAR);
DeviceString = AllocatePool(DeviceLength);
if (!DeviceString)
{
if (OldBuffer)
{
FreePool(OldBuffer);
}
return STATUS_INSUFFICIENT_RESOURCES;
}
/* Get the letter */
DeviceString[0] = SymlinkInformation->Name.Buffer[LETTER_POSITION];
DeviceString[1] = L':';
/* And copy the rest */
if (OldBuffer)
{
RtlCopyMemory(&DeviceString[2], OldBuffer, OldLength);
FreePool(OldBuffer);
}
TryWithVolumeName:
/* If we didn't find anything, try differently */
if (DeviceLength < 2 * sizeof(WCHAR) || DeviceString[1] != L':')
{
if (DeviceString)
{
FreePool(DeviceString);
DeviceLength = 0;
}
/* Try to find a volume name matching */
for (SymlinksEntry = DeviceInformation->SymbolicLinksListHead.Flink;
SymlinksEntry != &(DeviceInformation->SymbolicLinksListHead);
SymlinksEntry = SymlinksEntry->Flink)
{
SymlinkInformation = CONTAINING_RECORD(SymlinksEntry, SYMLINK_INFORMATION, SymbolicLinksListEntry);
if (MOUNTMGR_IS_VOLUME_NAME(&SymlinkInformation->Name))
{
break;
}
}
/* If found copy */
if (SymlinksEntry != &(DeviceInformation->SymbolicLinksListHead))
{
DeviceLength = SymlinkInformation->Name.Length;
DeviceString = AllocatePool(DeviceLength);
if (!DeviceString)
{
return STATUS_INSUFFICIENT_RESOURCES;
}
RtlCopyMemory(DeviceString, SymlinkInformation->Name.Buffer, DeviceLength);
/* Ensure we are in the right namespace; [1] can be ? */
DeviceString[1] = L'\\';
}
}
/* If we found something */
if (DeviceString)
{
/* At least, we will return our length */
((PMOUNTMGR_VOLUME_PATHS)Irp->AssociatedIrp.SystemBuffer)->MultiSzLength = DeviceLength;
/* MOUNTMGR_VOLUME_PATHS is a string + a ULONG */
Irp->IoStatus.Information = DeviceLength + sizeof(ULONG);
/* If we have enough room for copying the string */
if (sizeof(ULONG) + DeviceLength <= Stack->Parameters.DeviceIoControl.OutputBufferLength)
{
/* Copy it */
if (DeviceLength)
{
RtlCopyMemory(((PMOUNTMGR_VOLUME_PATHS)Irp->AssociatedIrp.SystemBuffer)->MultiSz, DeviceString, DeviceLength);
}
/* And double zero at its end - this is needed in case of multiple paths which are separated by a single 0 */
FreePool(DeviceString);
((PMOUNTMGR_VOLUME_PATHS)Irp->AssociatedIrp.SystemBuffer)->MultiSz[DeviceLength / sizeof(WCHAR)] = 0;
((PMOUNTMGR_VOLUME_PATHS)Irp->AssociatedIrp.SystemBuffer)->MultiSz[DeviceLength / sizeof(WCHAR) + 1] = 0;
return STATUS_SUCCESS;
}
else
{
/* Just return appropriate size and leave */
FreePool(DeviceString);
Irp->IoStatus.Information = sizeof(ULONG);
return STATUS_BUFFER_OVERFLOW;
}
}
/* Fail */
return STATUS_NOT_FOUND;
}
/*
* @implemented
*/
NTSTATUS
MountMgrValidateBackPointer(IN PASSOCIATED_DEVICE_ENTRY AssociatedDeviceEntry,
IN PDEVICE_INFORMATION DeviceInformation,
OUT PBOOLEAN Invalid)
{
HANDLE Handle;
NTSTATUS Status;
PLIST_ENTRY SymlinksEntry;
IO_STATUS_BLOCK IoStatusBlock;
PREPARSE_DATA_BUFFER ReparseData;
OBJECT_ATTRIBUTES ObjectAttributes;
UNICODE_STRING FullName, SubstituteName;
PSYMLINK_INFORMATION SymlinkInformation;
/* Initialize & allocate a string big enough to contain our complete mount point name */
FullName.Length = 0;
FullName.MaximumLength = AssociatedDeviceEntry->String.Length
+ AssociatedDeviceEntry->DeviceInformation->DeviceName.Length
+ sizeof(WCHAR)
+ sizeof(UNICODE_NULL);
FullName.Buffer = AllocatePool(FullName.MaximumLength);
if (!FullName.Buffer)
{
return STATUS_INSUFFICIENT_RESOURCES;
}
/* Create the path */
RtlAppendUnicodeStringToString(&FullName, &AssociatedDeviceEntry->DeviceInformation->DeviceName);
FullName.Buffer[FullName.Length / sizeof(WCHAR)] = L'\\';
RtlAppendUnicodeStringToString(&FullName, &AssociatedDeviceEntry->String);
FullName.Buffer[FullName.Length / sizeof(WCHAR)] = UNICODE_NULL;
/* Open it to query the reparse point */
InitializeObjectAttributes(&ObjectAttributes,
&FullName,
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
NULL,
NULL);
Status = ZwOpenFile(&Handle,
SYNCHRONIZE | FILE_READ_ATTRIBUTES,
&ObjectAttributes, &IoStatusBlock,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_REPARSE_POINT);
FreePool(FullName.Buffer);
if (!NT_SUCCESS(Status))
{
*Invalid = TRUE;
return STATUS_SUCCESS;
}
/* Allocate a buffer big enough to read reparse data */
ReparseData = AllocatePool(MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
if (ReparseData == NULL)
{
ZwClose(Handle);
return STATUS_INSUFFICIENT_RESOURCES;
}
/* Query reparse data */
Status = ZwFsControlFile(Handle,
NULL, NULL, NULL,
&IoStatusBlock,
FSCTL_GET_REPARSE_POINT,
NULL, 0,
ReparseData, MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
ZwClose(Handle);
if (!NT_SUCCESS(Status))
{
FreePool(ReparseData);
*Invalid = TRUE;
return STATUS_SUCCESS;
}
/* Create a string with the substitute name */
SubstituteName.Length = ReparseData->SymbolicLinkReparseBuffer.SubstituteNameLength;
SubstituteName.MaximumLength = SubstituteName.Length;
SubstituteName.Buffer = (PWSTR)((ULONG_PTR)ReparseData->SymbolicLinkReparseBuffer.PathBuffer + ReparseData->SymbolicLinkReparseBuffer.SubstituteNameOffset);
/* If that's a volume name that matches our associated device, that's a success! */
if (MOUNTMGR_IS_VOLUME_NAME(&SubstituteName))
{
if (SubstituteName.Length == 98 && SubstituteName.Buffer[1] == L'?')
{
for (SymlinksEntry = DeviceInformation->SymbolicLinksListHead.Flink;
SymlinksEntry != &(DeviceInformation->SymbolicLinksListHead);
SymlinksEntry = SymlinksEntry->Flink)
{
SymlinkInformation = CONTAINING_RECORD(SymlinksEntry, SYMLINK_INFORMATION, SymbolicLinksListEntry);
if (RtlEqualUnicodeString(&SubstituteName, &SymlinkInformation->Name, TRUE))
{
FreePool(ReparseData);
return STATUS_SUCCESS;
}
}
}
}
FreePool(ReparseData);
*Invalid = TRUE;
return STATUS_SUCCESS;
}
/*
* @implemented
*/
NTSTATUS
MountMgrQueryVolumePaths(IN PDEVICE_EXTENSION DeviceExtension,
IN PDEVICE_INFORMATION DeviceInformation,
IN PLIST_ENTRY DeviceInfoList,
OUT PMOUNTMGR_VOLUME_PATHS * VolumePaths,
OUT PDEVICE_INFORMATION *FailedDevice)
{
ULONG Written;
NTSTATUS Status;
PLIST_ENTRY Entry;
PSYMLINK_INFORMATION SymlinkInformation;
PDEVICE_INFORMATION_ENTRY DeviceInfoEntry;
PASSOCIATED_DEVICE_ENTRY AssociatedDeviceEntry;
PMOUNTMGR_VOLUME_PATHS * Paths = NULL, * CurrentPath;
ULONG OutputPathLength, NumberOfPaths, ReturnedPaths;
/* We return at least null char */
OutputPathLength = sizeof(UNICODE_NULL);
for (Entry = DeviceInformation->SymbolicLinksListHead.Flink;
Entry != &(DeviceInformation->SymbolicLinksListHead);
Entry = Entry->Flink)
{
SymlinkInformation = CONTAINING_RECORD(Entry, SYMLINK_INFORMATION, SymbolicLinksListEntry);
/* Try to find the drive letter (ie, DOS device) */
if (MOUNTMGR_IS_DRIVE_LETTER(&SymlinkInformation->Name) && SymlinkInformation->Online)
{
/* We'll return the letter */
OutputPathLength = 4 * sizeof(WCHAR);
break;
}
}
/* We didn't find any */
if (Entry == &(DeviceInformation->SymbolicLinksListHead))
{
SymlinkInformation = NULL;
}
/* Do we have any device info to return? */
for (Entry = DeviceInfoList->Flink; Entry != DeviceInfoList; Entry = Entry->Flink)
{
DeviceInfoEntry = CONTAINING_RECORD(Entry, DEVICE_INFORMATION_ENTRY, DeviceInformationEntry);
/* Matching current device */
if (DeviceInfoEntry->DeviceInformation == DeviceInformation)
{
/* Allocate the output buffer */
*VolumePaths = AllocatePool(sizeof(ULONG) + OutputPathLength);
if (*VolumePaths == NULL)
{
return STATUS_INSUFFICIENT_RESOURCES;
}
/* Set size */
(*VolumePaths)->MultiSzLength = OutputPathLength;
/* If we have a drive letter, return it */
if (SymlinkInformation != NULL)
{
(*VolumePaths)->MultiSz[0] = SymlinkInformation->Name.Buffer[LETTER_POSITION];
(*VolumePaths)->MultiSz[1] = L':';
(*VolumePaths)->MultiSz[2] = UNICODE_NULL;
(*VolumePaths)->MultiSz[3] = UNICODE_NULL;
}
else
{
(*VolumePaths)->MultiSz[0] = UNICODE_NULL;
}
return STATUS_SUCCESS;
}
}
/* Allocate a new device entry */
DeviceInfoEntry = AllocatePool(sizeof(DEVICE_INFORMATION_ENTRY));
if (DeviceInfoEntry == NULL)
{
return STATUS_INSUFFICIENT_RESOURCES;
}
/* Add it to the list */
DeviceInfoEntry->DeviceInformation = DeviceInformation;
InsertTailList(DeviceInfoList, &DeviceInfoEntry->DeviceInformationEntry);
NumberOfPaths = 0;
/* Count the amount of devices we will have to handle */
if (!IsListEmpty(&DeviceInformation->AssociatedDevicesHead))
{
for (Entry = DeviceInformation->AssociatedDevicesHead.Flink;
Entry != &DeviceInformation->AssociatedDevicesHead;
Entry = Entry->Flink)
{
++NumberOfPaths;
}
ASSERT(NumberOfPaths != 0);
/* And allocate a big enough buffer */
Paths = AllocatePool(NumberOfPaths * sizeof(PMOUNTMGR_VOLUME_PATHS));
if (Paths == NULL)
{
RemoveEntryList(&DeviceInfoEntry->DeviceInformationEntry);
FreePool(DeviceInfoEntry);
return STATUS_INSUFFICIENT_RESOURCES;
}
}
/* Start the hot loop to gather all the paths and be able to compute total output length! */
ReturnedPaths = 0;
CurrentPath = Paths;
for (Entry = DeviceInformation->AssociatedDevicesHead.Flink;
Entry != &DeviceInformation->AssociatedDevicesHead;
Entry = Entry->Flink)
{
USHORT InnerStrings;
BOOLEAN Invalid = FALSE;
AssociatedDeviceEntry = CONTAINING_RECORD(Entry, ASSOCIATED_DEVICE_ENTRY, AssociatedDevicesEntry);
/* Validate the fact its a mount point by query reparse data */
Status = MountMgrValidateBackPointer(AssociatedDeviceEntry, DeviceInformation, &Invalid);
/* If we found an invalid device, that's a failure */
if (Invalid)
{
*FailedDevice = AssociatedDeviceEntry->DeviceInformation;
Status = STATUS_UNSUCCESSFUL;
}
/* Check whether we failed, if so, bail out */
if (!NT_SUCCESS(Status))
{
ULONG i;
for (i = 0; i < ReturnedPaths; ++i)
{
FreePool(Paths[i]);
}
if (Paths != NULL)
{
FreePool(Paths);
}
RemoveEntryList(&DeviceInfoEntry->DeviceInformationEntry);
FreePool(DeviceInfoEntry);
return Status;
}
/* Query associated paths (hello ourselves :-)) */
Status = MountMgrQueryVolumePaths(DeviceExtension,
AssociatedDeviceEntry->DeviceInformation,
DeviceInfoList,
CurrentPath,
FailedDevice);
if (!NT_SUCCESS(Status))
{
ULONG i;
for (i = 0; i < ReturnedPaths; ++i)
{
FreePool(Paths[i]);
}
if (Paths != NULL)
{
FreePool(Paths);
}
RemoveEntryList(&DeviceInfoEntry->DeviceInformationEntry);
FreePool(DeviceInfoEntry);
return Status;
}
/* Count the number of strings we have in the multi string buffer */
InnerStrings = 0;
if ((*CurrentPath)->MultiSzLength != sizeof(UNICODE_NULL))
{
ULONG i;
PWSTR MultiSz = (*CurrentPath)->MultiSz;
for (i = 0; i < (*CurrentPath)->MultiSzLength / sizeof(WCHAR); ++i, ++MultiSz)
{
if (*MultiSz == UNICODE_NULL)
{
++InnerStrings;
}
}
}
/* We returned one more path (ie, one more allocated buffer) */
++ReturnedPaths;
/* Move the next pointer to use in the array */
++CurrentPath;
/* Multiply String.Length by the number of found paths, we always add it after a path */
OutputPathLength += (*CurrentPath)->MultiSzLength + InnerStrings * AssociatedDeviceEntry->String.Length - sizeof(UNICODE_NULL);
}
/* Allocate the output buffer */
*VolumePaths = AllocatePool(sizeof(ULONG) + OutputPathLength);
if (*VolumePaths == NULL)
{
ULONG i;
for (i = 0; i < ReturnedPaths; ++i)
{
FreePool(Paths[i]);
}
if (Paths != NULL)
{
FreePool(Paths);
}
RemoveEntryList(&DeviceInfoEntry->DeviceInformationEntry);
FreePool(DeviceInfoEntry);
return STATUS_INSUFFICIENT_RESOURCES;
}
Written = 0;
/* If we had found a DOS letter, that's the first thing we return */
(*VolumePaths)->MultiSzLength = OutputPathLength;
if (SymlinkInformation != NULL)
{
(*VolumePaths)->MultiSz[0] = SymlinkInformation->Name.Buffer[LETTER_POSITION];
(*VolumePaths)->MultiSz[1] = L':';
(*VolumePaths)->MultiSz[2] = UNICODE_NULL;
Written = 3;
}
/* Now, browse again all our paths to return them */
CurrentPath = Paths;
for (Entry = DeviceInformation->AssociatedDevicesHead.Flink;
Entry != &DeviceInformation->AssociatedDevicesHead;
Entry = Entry->Flink)
{
AssociatedDeviceEntry = CONTAINING_RECORD(Entry, ASSOCIATED_DEVICE_ENTRY, AssociatedDevicesEntry);
/* If we had a path... */
if ((*CurrentPath)->MultiSzLength != sizeof(UNICODE_NULL))
{
ULONG i, Offset;
PWSTR MultiSz;
/* This offset is used to "jump" into MultiSz, so, start with the string begin (ie, skip MultiSzLength) */
Offset = sizeof(ULONG);
/* Browse every single letter, and skip last UNICODE_NULL */
for (i = 0; i < (*CurrentPath)->MultiSzLength / sizeof(WCHAR) - 1; ++i)
{
/* Get the letter */
MultiSz = (PWSTR)((ULONG_PTR)(*CurrentPath) + Offset);
/* If it was part of the path, just return it */
if (*MultiSz != UNICODE_NULL)
{
(*VolumePaths)->MultiSz[Written] = *MultiSz;
}
else
{
/* Otherwise, as planed, return our whole associated device name */
RtlCopyMemory(&(*VolumePaths)->MultiSz[Written],
AssociatedDeviceEntry->String.Buffer,
AssociatedDeviceEntry->String.Length);
Written += AssociatedDeviceEntry->String.Length / sizeof(WCHAR);
/* And don't forget to nullify */
(*VolumePaths)->MultiSz[Written] = UNICODE_NULL;
}
/* We at least return a letter or a null char */
++Written;
/* Move to the next letter */
Offset += sizeof(WCHAR);
}
}
FreePool(*CurrentPath);
++CurrentPath;
}
/* MultiSz: don't forget last null char */
(*VolumePaths)->MultiSz[Written] = UNICODE_NULL;
/* Cleanup everything and return success! */
if (Paths != NULL)
{
FreePool(Paths);
}
RemoveEntryList(&DeviceInfoEntry->DeviceInformationEntry);
FreePool(DeviceInfoEntry);
return STATUS_SUCCESS;
}
/*
* @implemented
*/
NTSTATUS
MountMgrQueryDosVolumePaths(IN PDEVICE_EXTENSION DeviceExtension,
IN PIRP Irp)
{
NTSTATUS Status;
PLIST_ENTRY Entry;
LIST_ENTRY Devices;
BOOLEAN NeedNotification;
PIO_STACK_LOCATION Stack;
UNICODE_STRING SymbolicName;
ULONG Attempts, OutputLength;
PMOUNTMGR_TARGET_NAME Target;
PMOUNTMGR_VOLUME_PATHS Paths, Output;
RECONCILE_WORK_ITEM_CONTEXT ReconcileContext;
PDEVICE_INFORMATION DeviceInformation, ListDeviceInfo, FailedDevice;
Stack = IoGetCurrentIrpStackLocation(Irp);
/* Validate input size */
if (Stack->Parameters.DeviceIoControl.InputBufferLength < sizeof(MOUNTMGR_TARGET_NAME))
{
return STATUS_INVALID_PARAMETER;
}
/* Ensure we have received UNICODE_STRING */
Target = (PMOUNTMGR_TARGET_NAME)Irp->AssociatedIrp.SystemBuffer;
if (Target->DeviceNameLength & 1)
{
return STATUS_INVALID_PARAMETER;
}
/* Validate the entry structure size */
if (Target->DeviceNameLength + FIELD_OFFSET(MOUNTMGR_TARGET_NAME, DeviceName) > Stack->Parameters.DeviceIoControl.InputBufferLength)
{
return STATUS_INVALID_PARAMETER;
}
/* Ensure we can at least return needed size */
if (Stack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(ULONG))
{
return STATUS_INVALID_PARAMETER;
}
/* Construct string for query */
SymbolicName.Length = Target->DeviceNameLength;
SymbolicName.MaximumLength = Target->DeviceNameLength + sizeof(UNICODE_NULL);
SymbolicName.Buffer = Target->DeviceName;
/* Find device with our info */
Status = FindDeviceInfo(DeviceExtension, &SymbolicName, FALSE, &DeviceInformation);
if (!NT_SUCCESS(Status))
{
return Status;
}
NeedNotification = FALSE;
Attempts = 0;
for (;;)
{
FailedDevice = NULL;
InitializeListHead(&Devices);
/* Query paths */
Status = MountMgrQueryVolumePaths(DeviceExtension, DeviceInformation, &Devices, &Paths, &FailedDevice);
if (NT_SUCCESS(Status))
{
break;
}
/* If it failed for generic reason (memory, whatever), bail out (ie, FailedDevice not set) */
if (FailedDevice == NULL)
{
return Status;
}
/* If PnP, let's notify in case of success */
if (!DeviceInformation->ManuallyRegistered)
{
NeedNotification = TRUE;
}
/* Reconcile database */
ReconcileContext.DeviceExtension = DeviceExtension;
ReconcileContext.DeviceInformation = FailedDevice;
KeReleaseSemaphore(&DeviceExtension->DeviceLock, IO_NO_INCREMENT, 1, FALSE);
ReconcileThisDatabaseWithMasterWorker(&ReconcileContext);
KeWaitForSingleObject(&DeviceExtension->DeviceLock, Executive, KernelMode, FALSE, NULL);
/* Look for our device, to check it's online */
for (Entry = DeviceExtension->DeviceListHead.Flink;
Entry != &DeviceExtension->DeviceListHead;
Entry = Entry->Flink)
{
ListDeviceInfo = CONTAINING_RECORD(Entry, DEVICE_INFORMATION, DeviceListEntry);
/* It's online, it's OK! */
if (ListDeviceInfo == DeviceInformation)
{
break;
}
}
/* It's not online, it's not good */
if (Entry == &DeviceExtension->DeviceListHead)
{
return STATUS_OBJECT_NAME_NOT_FOUND;
}
/* Increase attempts count */
++Attempts;
/* Don't look forever and fail if we get out of attempts */
if (Attempts >= 1000)
{
return Status;
}
}
/* We need to notify? Go ahead */
if (NeedNotification)
{
MountMgrNotifyNameChange(DeviceExtension, &SymbolicName, FALSE);
}
/* Get output buffer */
Output = (PMOUNTMGR_VOLUME_PATHS)Irp->AssociatedIrp.SystemBuffer;
/* Set required size */
Output->MultiSzLength = Paths->MultiSzLength;
/* Compute total length */
OutputLength = Output->MultiSzLength + sizeof(ULONG);
/* If it cannot fit, just return need size and quit */
if (OutputLength > Stack->Parameters.DeviceIoControl.OutputBufferLength)
{
Irp->IoStatus.Information = sizeof(ULONG);
FreePool(Paths);
return STATUS_BUFFER_OVERFLOW;
}
/* Copy data and quit */
Irp->IoStatus.Information = OutputLength;
RtlCopyMemory(Output->MultiSz, Paths->MultiSz, Output->MultiSzLength);
FreePool(Paths);
return STATUS_SUCCESS;
}
/*
* @implemented
*/
NTSTATUS
MountMgrKeepLinksWhenOffline(IN PDEVICE_EXTENSION DeviceExtension,
IN PIRP Irp)
{
NTSTATUS Status;
PIO_STACK_LOCATION Stack;
UNICODE_STRING SymbolicName;
PMOUNTMGR_TARGET_NAME Target;
PDEVICE_INFORMATION DeviceInformation;
Stack = IoGetCurrentIrpStackLocation(Irp);
/* Validate input */
if (Stack->Parameters.DeviceIoControl.InputBufferLength < sizeof(MOUNTMGR_TARGET_NAME))
{
return STATUS_INVALID_PARAMETER;
}
Target = (PMOUNTMGR_TARGET_NAME)Irp->AssociatedIrp.SystemBuffer;
if (Target->DeviceNameLength + sizeof(USHORT) > Stack->Parameters.DeviceIoControl.InputBufferLength)
{
return STATUS_INVALID_PARAMETER;
}
SymbolicName.Length =
SymbolicName.MaximumLength = Target->DeviceNameLength;
SymbolicName.Buffer = Target->DeviceName;
/* Find the associated device */
Status = FindDeviceInfo(DeviceExtension, &SymbolicName, FALSE, &DeviceInformation);
if (!NT_SUCCESS(Status))
{
return Status;
}
/* Mark we want to keep links */
DeviceInformation->KeepLinks = TRUE;
return STATUS_SUCCESS;
}
/*
* @implemented
*/
NTSTATUS
MountMgrVolumeArrivalNotification(IN PDEVICE_EXTENSION DeviceExtension,
IN PIRP Irp)
{
NTSTATUS Status;
BOOLEAN OldState;
PIO_STACK_LOCATION Stack;
UNICODE_STRING SymbolicName;
PMOUNTMGR_TARGET_NAME Target;
Stack = IoGetCurrentIrpStackLocation(Irp);
/* Validate input */
if (Stack->Parameters.DeviceIoControl.InputBufferLength < sizeof(MOUNTMGR_TARGET_NAME))
{
return STATUS_INVALID_PARAMETER;
}
Target = (PMOUNTMGR_TARGET_NAME)Irp->AssociatedIrp.SystemBuffer;
if (Target->DeviceNameLength + sizeof(USHORT) > Stack->Parameters.DeviceIoControl.InputBufferLength)
{
return STATUS_INVALID_PARAMETER;
}
SymbolicName.Length =
SymbolicName.MaximumLength = Target->DeviceNameLength;
SymbolicName.Buffer = Target->DeviceName;
/* Disable hard errors */
OldState = PsGetThreadHardErrorsAreDisabled(PsGetCurrentThread());
PsSetThreadHardErrorsAreDisabled(PsGetCurrentThread(), TRUE);
/* Call real worker */
Status = MountMgrMountedDeviceArrival(DeviceExtension, &SymbolicName, TRUE);
PsSetThreadHardErrorsAreDisabled(PsGetCurrentThread(), OldState);
return Status;
}
/*
* @implemented
*/
NTSTATUS
MountMgrQueryPoints(IN PDEVICE_EXTENSION DeviceExtension,
IN PIRP Irp)
{
NTSTATUS Status;
PIO_STACK_LOCATION Stack;
PMOUNTDEV_UNIQUE_ID UniqueId;
PMOUNTMGR_MOUNT_POINT MountPoint;
UNICODE_STRING SymbolicName, DeviceName;
Stack = IoGetCurrentIrpStackLocation(Irp);
/* Validate input... */
if (Stack->Parameters.DeviceIoControl.InputBufferLength < sizeof(MOUNTMGR_MOUNT_POINT))
{
return STATUS_INVALID_PARAMETER;
}
MountPoint = (PMOUNTMGR_MOUNT_POINT)Irp->AssociatedIrp.SystemBuffer;
if (!MountPoint->SymbolicLinkNameLength)
{
MountPoint->SymbolicLinkNameOffset = 0;
}
if (!MountPoint->UniqueIdLength)
{
MountPoint->UniqueIdOffset = 0;
}
if (!MountPoint->DeviceNameLength)
{
MountPoint->DeviceNameOffset = 0;
}
/* Addresses can't be odd */
if ((MountPoint->SymbolicLinkNameOffset & 1) ||
(MountPoint->SymbolicLinkNameLength & 1))
{
return STATUS_INVALID_PARAMETER;
}
if ((MountPoint->UniqueIdOffset & 1) ||
(MountPoint->UniqueIdLength & 1))
{
return STATUS_INVALID_PARAMETER;
}
if ((MountPoint->DeviceNameOffset & 1) ||
(MountPoint->DeviceNameLength & 1))
{
return STATUS_INVALID_PARAMETER;
}
/* We can't go beyond */
if (((ULONG)MountPoint->SymbolicLinkNameLength + MountPoint->UniqueIdLength +
MountPoint->DeviceNameLength) > Stack->Parameters.DeviceIoControl.InputBufferLength)
{
return STATUS_INVALID_PARAMETER;
}
if (Stack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(MOUNTMGR_MOUNT_POINTS))
{
return STATUS_INVALID_PARAMETER;
}
/* If caller provided a Symlink, use it */
if (MountPoint->SymbolicLinkNameLength != 0)
{
if (MountPoint->SymbolicLinkNameLength > MAXSHORT)
{
return STATUS_INVALID_PARAMETER;
}
SymbolicName.Length = MountPoint->SymbolicLinkNameLength;
SymbolicName.MaximumLength = MountPoint->SymbolicLinkNameLength + sizeof(WCHAR);
SymbolicName.Buffer = AllocatePool(SymbolicName.MaximumLength);
if (!SymbolicName.Buffer)
{
return STATUS_INSUFFICIENT_RESOURCES;
}
RtlCopyMemory(SymbolicName.Buffer,
(PWSTR)((ULONG_PTR)MountPoint + MountPoint->SymbolicLinkNameOffset),
SymbolicName.Length);
SymbolicName.Buffer[SymbolicName.Length / sizeof(WCHAR)] = UNICODE_NULL;
/* Query links using it */
Status = QueryPointsFromSymbolicLinkName(DeviceExtension, &SymbolicName, Irp);
FreePool(SymbolicName.Buffer);
}
/* If user provided an unique ID */
else if (MountPoint->UniqueIdLength != 0)
{
UniqueId = AllocatePool(MountPoint->UniqueIdLength + sizeof(MOUNTDEV_UNIQUE_ID));
if (!UniqueId)
{
return STATUS_INSUFFICIENT_RESOURCES;
}
UniqueId->UniqueIdLength = MountPoint->UniqueIdLength;
RtlCopyMemory(UniqueId->UniqueId,
(PVOID)((ULONG_PTR)MountPoint + MountPoint->UniqueIdOffset),
MountPoint->UniqueIdLength);
/* Query links using it */
Status = QueryPointsFromMemory(DeviceExtension, Irp, UniqueId, NULL);
FreePool(UniqueId);
}
/* If caller provided a device name */
else if (MountPoint->DeviceNameLength != 0)
{
if (MountPoint->DeviceNameLength > MAXSHORT)
{
return STATUS_INVALID_PARAMETER;
}
DeviceName.Length = MountPoint->DeviceNameLength;
DeviceName.MaximumLength = MountPoint->DeviceNameLength + sizeof(WCHAR);
DeviceName.Buffer = AllocatePool(DeviceName.MaximumLength);
if (!DeviceName.Buffer)
{
return STATUS_INSUFFICIENT_RESOURCES;
}
RtlCopyMemory(DeviceName.Buffer,
(PWSTR)((ULONG_PTR)MountPoint + MountPoint->DeviceNameOffset),
DeviceName.Length);
DeviceName.Buffer[DeviceName.Length / sizeof(WCHAR)] = UNICODE_NULL;
/* Query links using it */
Status = QueryPointsFromMemory(DeviceExtension, Irp, NULL, &DeviceName);
FreePool(DeviceName.Buffer);
}
else
{
/* Otherwise, query all links */
Status = QueryPointsFromMemory(DeviceExtension, Irp, NULL, NULL);
}
return Status;
}
/*
* @implemented
*/
NTSTATUS
MountMgrDeletePoints(IN PDEVICE_EXTENSION DeviceExtension,
IN PIRP Irp)
{
ULONG Link;
NTSTATUS Status;
BOOLEAN CreateNoDrive;
PIO_STACK_LOCATION Stack;
PMOUNTDEV_UNIQUE_ID UniqueId;
PMOUNTMGR_MOUNT_POINT MountPoint;
PMOUNTMGR_MOUNT_POINTS MountPoints;
UNICODE_STRING SymbolicName, DeviceName;
Stack = IoGetCurrentIrpStackLocation(Irp);
/* Validate input */
if (Stack->Parameters.DeviceIoControl.InputBufferLength < sizeof(MOUNTMGR_MOUNT_POINT))
{
return STATUS_INVALID_PARAMETER;
}
/* Query points */
MountPoint = (PMOUNTMGR_MOUNT_POINT)Irp->AssociatedIrp.SystemBuffer;
CreateNoDrive = (MountPoint->SymbolicLinkNameOffset && MountPoint->SymbolicLinkNameLength);
Status = MountMgrQueryPoints(DeviceExtension, Irp);
if (!NT_SUCCESS(Status))
{
return Status;
}
/* For all the points matching the request */
MountPoints = (PMOUNTMGR_MOUNT_POINTS)Irp->AssociatedIrp.SystemBuffer;
for (Link = 0; Link < MountPoints->NumberOfMountPoints; Link++)
{
SymbolicName.Length = MountPoints->MountPoints[Link].SymbolicLinkNameLength;
SymbolicName.MaximumLength = SymbolicName.Length + sizeof(WCHAR);
SymbolicName.Buffer = AllocatePool(SymbolicName.MaximumLength);
if (!SymbolicName.Buffer)
{
return STATUS_INSUFFICIENT_RESOURCES;
}
RtlCopyMemory(SymbolicName.Buffer,
(PWSTR)((ULONG_PTR)MountPoints + MountPoints->MountPoints[Link].SymbolicLinkNameOffset),
SymbolicName.Length);
SymbolicName.Buffer[SymbolicName.Length / sizeof(WCHAR)] = UNICODE_NULL;
/* Create a no drive entry for the drive letters */
if (CreateNoDrive && IsDriveLetter(&SymbolicName))
{
UniqueId = AllocatePool(MountPoints->MountPoints[Link].UniqueIdLength + sizeof(MOUNTDEV_UNIQUE_ID));
if (UniqueId)
{
UniqueId->UniqueIdLength = MountPoints->MountPoints[Link].UniqueIdLength;
RtlCopyMemory(UniqueId->UniqueId,
(PMOUNTDEV_UNIQUE_ID)((ULONG_PTR)MountPoints + MountPoints->MountPoints[Link].UniqueIdOffset),
MountPoints->MountPoints[Link].UniqueIdLength);
CreateNoDriveLetterEntry(UniqueId);
FreePool(UniqueId);
}
}
/* If there are no link any more, and no need to create a no drive entry */
if (Link == 0 && !CreateNoDrive)
{
/* Then, delete everything */
UniqueId = AllocatePool(MountPoints->MountPoints[Link].UniqueIdLength);
if (UniqueId)
{
RtlCopyMemory(UniqueId,
(PMOUNTDEV_UNIQUE_ID)((ULONG_PTR)MountPoints + MountPoints->MountPoints[Link].UniqueIdOffset),
MountPoints->MountPoints[Link].UniqueIdLength);
DeleteNoDriveLetterEntry(UniqueId);
FreePool(UniqueId);
}
}
/* Delete all the information about the mount point */
GlobalDeleteSymbolicLink(&SymbolicName);
DeleteSymbolicLinkNameFromMemory(DeviceExtension, &SymbolicName, FALSE);
RtlDeleteRegistryValue(RTL_REGISTRY_ABSOLUTE, DatabasePath, SymbolicName.Buffer);
FreePool(SymbolicName.Buffer);
/* Notify the change */
DeviceName.Length = DeviceName.MaximumLength =
MountPoints->MountPoints[Link].DeviceNameLength;
DeviceName.Buffer = (PWSTR)((ULONG_PTR)MountPoints + MountPoints->MountPoints[Link].DeviceNameOffset);
MountMgrNotifyNameChange(DeviceExtension, &DeviceName, TRUE);
}
MountMgrNotify(DeviceExtension);
return Status;
}
/*
* @implemented
*/
NTSTATUS
MountMgrDeletePointsDbOnly(IN PDEVICE_EXTENSION DeviceExtension,
IN PIRP Irp)
{
ULONG Link;
NTSTATUS Status;
UNICODE_STRING SymbolicName;
PMOUNTDEV_UNIQUE_ID UniqueId;
PMOUNTMGR_MOUNT_POINTS MountPoints;
/* Query points */
Status = MountMgrQueryPoints(DeviceExtension, Irp);
if (!NT_SUCCESS(Status))
{
return Status;
}
MountPoints = (PMOUNTMGR_MOUNT_POINTS)Irp->AssociatedIrp.SystemBuffer;
if (MountPoints->NumberOfMountPoints == 0)
{
return Status;
}
/* For all the mount points */
for (Link = 0; Link < MountPoints->NumberOfMountPoints; Link++)
{
SymbolicName.Length = MountPoints->MountPoints[Link].SymbolicLinkNameLength;
SymbolicName.MaximumLength = SymbolicName.Length + sizeof(WCHAR);
SymbolicName.Buffer = AllocatePool(SymbolicName.MaximumLength);
if (!SymbolicName.Buffer)
{
return STATUS_INSUFFICIENT_RESOURCES;
}
RtlCopyMemory(SymbolicName.Buffer,
(PWSTR)((ULONG_PTR)MountPoints + MountPoints->MountPoints[Link].SymbolicLinkNameOffset),
SymbolicName.Length);
SymbolicName.Buffer[SymbolicName.Length / sizeof(WCHAR)] = UNICODE_NULL;
/* If the only mount point is a drive letter, then create a no letter drive entry */
if (MountPoints->NumberOfMountPoints == 1 && IsDriveLetter(&SymbolicName))
{
UniqueId = AllocatePool(MountPoints->MountPoints[Link].UniqueIdLength + sizeof(MOUNTDEV_UNIQUE_ID));
if (UniqueId)
{
UniqueId->UniqueIdLength = MountPoints->MountPoints[Link].UniqueIdLength;
RtlCopyMemory(UniqueId->UniqueId,
(PMOUNTDEV_UNIQUE_ID)((ULONG_PTR)MountPoints + MountPoints->MountPoints[Link].UniqueIdOffset),
MountPoints->MountPoints[Link].UniqueIdLength);
CreateNoDriveLetterEntry(UniqueId);
FreePool(UniqueId);
}
}
/* Simply delete mount point from DB */
DeleteSymbolicLinkNameFromMemory(DeviceExtension, &SymbolicName, TRUE);
RtlDeleteRegistryValue(RTL_REGISTRY_ABSOLUTE, DatabasePath, SymbolicName.Buffer);
FreePool(SymbolicName.Buffer);
}
return Status;
}
/*
* @implemented
*/
NTSTATUS
MountMgrVolumeMountPointChanged(IN PDEVICE_EXTENSION DeviceExtension,
IN PIRP Irp,
IN NTSTATUS LockStatus,
OUT PUNICODE_STRING SourceDeviceName,
OUT PUNICODE_STRING SourceSymbolicName,
OUT PUNICODE_STRING TargetVolumeName)
{
HANDLE Handle;
NTSTATUS Status;
PFILE_OBJECT FileObject;
PIO_STACK_LOCATION Stack;
ULONG Length, SavedLength;
BOOLEAN FOReferenced = FALSE;
IO_STATUS_BLOCK IoStatusBlock;
OBJECT_ATTRIBUTES ObjectAttributes;
PDEVICE_INFORMATION DeviceInformation;
OBJECT_NAME_INFORMATION ObjectNameInfo;
FILE_FS_DEVICE_INFORMATION FsDeviceInfo;
PFILE_NAME_INFORMATION FileNameInfo = NULL;
PMOUNTMGR_VOLUME_MOUNT_POINT VolumeMountPoint;
POBJECT_NAME_INFORMATION ObjectNameInfoPtr = NULL;
UNICODE_STRING SourceVolumeName, TargetDeviceName;
Stack = IoGetCurrentIrpStackLocation(Irp);
/* Validate input */
if (Stack->Parameters.DeviceIoControl.InputBufferLength < sizeof(MOUNTMGR_VOLUME_MOUNT_POINT))
{
return STATUS_INVALID_PARAMETER;
}
VolumeMountPoint = (PMOUNTMGR_VOLUME_MOUNT_POINT)Irp->AssociatedIrp.SystemBuffer;
if (((ULONG)VolumeMountPoint->SourceVolumeNameLength + VolumeMountPoint->TargetVolumeNameLength) <
Stack->Parameters.DeviceIoControl.InputBufferLength)
{
return STATUS_INVALID_PARAMETER;
}
/* Get source volume name */
SourceVolumeName.Length =
SourceVolumeName.MaximumLength = VolumeMountPoint->SourceVolumeNameLength;
SourceVolumeName.Buffer = (PWSTR)((ULONG_PTR)VolumeMountPoint + VolumeMountPoint->SourceVolumeNameOffset);
InitializeObjectAttributes(&ObjectAttributes,
&SourceVolumeName,
OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE,
NULL,
NULL);
/* Open it */
Status = ZwOpenFile(&Handle,
SYNCHRONIZE | FILE_READ_ATTRIBUTES,
&ObjectAttributes,
&IoStatusBlock,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_REPARSE_POINT);
if (!NT_SUCCESS(Status))
{
return Status;
}
TargetDeviceName.Buffer = NULL;
/* Query its attributes */
Status = ZwQueryVolumeInformationFile(Handle,
&IoStatusBlock,
&FsDeviceInfo,
sizeof(FsDeviceInfo),
FileFsDeviceInformation);
if (!NT_SUCCESS(Status))
{
goto Cleanup;
}
if (FsDeviceInfo.DeviceType != FILE_DEVICE_DISK && FsDeviceInfo.DeviceType != FILE_DEVICE_VIRTUAL_DISK)
{
goto Cleanup;
}
if (FsDeviceInfo.Characteristics != (FILE_REMOTE_DEVICE | FILE_REMOVABLE_MEDIA))
{
goto Cleanup;
}
/* Reference it */
Status = ObReferenceObjectByHandle(Handle, 0, *IoFileObjectType, KernelMode, (PVOID *)&FileObject, NULL);
if (!NT_SUCCESS(Status))
{
goto Cleanup;
}
FOReferenced = TRUE;
/* Get file name */
FileNameInfo = AllocatePool(sizeof(FILE_NAME_INFORMATION));
if (!FileNameInfo)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Cleanup;
}
Status = ZwQueryInformationFile(Handle, &IoStatusBlock, FileNameInfo,
sizeof(FILE_NAME_INFORMATION),
FileNameInformation);
if (Status == STATUS_BUFFER_OVERFLOW)
{
/* Now we have real length, use it */
Length = FileNameInfo->FileNameLength;
FreePool(FileNameInfo);
FileNameInfo = AllocatePool(sizeof(FILE_NAME_INFORMATION) + Length);
if (!FileNameInfo)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Cleanup;
}
/* Really query file name */
Status = ZwQueryInformationFile(Handle, &IoStatusBlock, FileNameInfo,
sizeof(FILE_NAME_INFORMATION) + Length,
FileNameInformation);
}
if (!NT_SUCCESS(Status))
{
goto Cleanup;
}
/* Get symbolic name */
ObjectNameInfoPtr = &ObjectNameInfo;
SavedLength = sizeof(OBJECT_NAME_INFORMATION);
Status = ObQueryNameString(FileObject->DeviceObject, ObjectNameInfoPtr, sizeof(OBJECT_NAME_INFORMATION), &Length);
if (Status == STATUS_INFO_LENGTH_MISMATCH)
{
/* Once again, with proper size, it works better */
ObjectNameInfoPtr = AllocatePool(Length);
if (!ObjectNameInfoPtr)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Cleanup;
}
SavedLength = Length;
Status = ObQueryNameString(FileObject->DeviceObject, ObjectNameInfoPtr, SavedLength, &Length);
}
if (!NT_SUCCESS(Status))
{
goto Cleanup;
}
/* Now, query the device name */
Status = QueryDeviceInformation(&ObjectNameInfoPtr->Name, SourceDeviceName,
NULL, NULL, NULL, NULL, NULL, NULL);
if (!NT_SUCCESS(Status))
{
goto Cleanup;
}
/* For target volume name, use input */
TargetVolumeName->Length =
TargetVolumeName->MaximumLength = VolumeMountPoint->TargetVolumeNameLength;
TargetVolumeName->Buffer = (PWSTR)((ULONG_PTR)VolumeMountPoint + VolumeMountPoint->TargetVolumeNameOffset);
/* Query its device name */
Status = QueryDeviceInformation(TargetVolumeName, &TargetDeviceName,
NULL, NULL, NULL, NULL, NULL, NULL);
if (!NT_SUCCESS(Status))
{
goto Cleanup;
}
/* Return symbolic name */
SourceSymbolicName->Length =
SourceSymbolicName->MaximumLength = (USHORT)FileNameInfo->FileNameLength;
SourceSymbolicName->Buffer = (PWSTR)FileNameInfo;
/* memmove allows memory overlap */
RtlMoveMemory(SourceSymbolicName->Buffer, FileNameInfo->FileName, SourceSymbolicName->Length);
FileNameInfo = NULL;
/* Notify the change */
MountMgrNotify(DeviceExtension);
MountMgrNotifyNameChange(DeviceExtension, &TargetDeviceName, TRUE);
/* If we are locked, sync databases if possible */
if (NT_SUCCESS(LockStatus))
{
Status = FindDeviceInfo(DeviceExtension, SourceDeviceName, FALSE, &DeviceInformation);
if (NT_SUCCESS(Status))
{
ReconcileThisDatabaseWithMaster(DeviceExtension, DeviceInformation);
}
else
{
Status = STATUS_PENDING;
}
}
Cleanup:
if (TargetDeviceName.Buffer)
{
FreePool(TargetDeviceName.Buffer);
}
if (ObjectNameInfoPtr && ObjectNameInfoPtr != &ObjectNameInfo)
{
FreePool(ObjectNameInfoPtr);
}
if (FileNameInfo)
{
FreePool(FileNameInfo);
}
if (FOReferenced)
{
ObDereferenceObject(FileObject);
}
return Status;
}
/*
* @implemented
*/
NTSTATUS
MountMgrVolumeMountPointCreated(IN PDEVICE_EXTENSION DeviceExtension,
IN PIRP Irp,
IN NTSTATUS LockStatus)
{
LONG Offset;
BOOLEAN Found;
NTSTATUS Status;
HANDLE RemoteDatabase;
PMOUNTDEV_UNIQUE_ID UniqueId;
PDATABASE_ENTRY DatabaseEntry;
PASSOCIATED_DEVICE_ENTRY AssociatedEntry;
PDEVICE_INFORMATION DeviceInformation, TargetDeviceInformation;
UNICODE_STRING LinkTarget, SourceDeviceName, SourceSymbolicName, TargetVolumeName, VolumeName, DbName;
/* Initialize string */
LinkTarget.Length = 0;
LinkTarget.MaximumLength = 0xC8;
LinkTarget.Buffer = AllocatePool(LinkTarget.MaximumLength);
if (LinkTarget.Buffer == NULL)
{
return STATUS_INSUFFICIENT_RESOURCES;
}
/* If the mount point was created, then, it changed!
* Also use it to query some information
*/
Status = MountMgrVolumeMountPointChanged(DeviceExtension, Irp, LockStatus, &SourceDeviceName, &SourceSymbolicName, &TargetVolumeName);
/* Pending means DB are under synchronization, bail out */
if (Status == STATUS_PENDING)
{
FreePool(LinkTarget.Buffer);
FreePool(SourceDeviceName.Buffer);
FreePool(SourceSymbolicName.Buffer);
return STATUS_SUCCESS;
}
else if (!NT_SUCCESS(Status))
{
FreePool(LinkTarget.Buffer);
return Status;
}
/* Query the device information */
Status = FindDeviceInfo(DeviceExtension, &SourceDeviceName, FALSE, &DeviceInformation);
if (!NT_SUCCESS(Status))
{
/* If it failed, first try to get volume name */
Status = QueryVolumeName(0, NULL, &SourceDeviceName, &LinkTarget, &VolumeName);
if (!NT_SUCCESS(Status))
{
/* Then, try to read the symlink */
Status = MountMgrQuerySymbolicLink(&SourceDeviceName, &LinkTarget);
if (!NT_SUCCESS(Status))
{
FreePool(LinkTarget.Buffer);
FreePool(SourceDeviceName.Buffer);
FreePool(SourceSymbolicName.Buffer);
return Status;
}
}
else
{
FreePool(VolumeName.Buffer);
}
FreePool(SourceDeviceName.Buffer);
SourceDeviceName.Length = LinkTarget.Length;
SourceDeviceName.MaximumLength = LinkTarget.MaximumLength;
SourceDeviceName.Buffer = LinkTarget.Buffer;
/* Now that we have the correct source, reattempt to query information */
Status = FindDeviceInfo(DeviceExtension, &SourceDeviceName, FALSE, &DeviceInformation);
if (!NT_SUCCESS(Status))
{
FreePool(SourceDeviceName.Buffer);
FreePool(SourceSymbolicName.Buffer);
return Status;
}
}
FreePool(SourceDeviceName.Buffer);
/* Get information about target device */
Status = FindDeviceInfo(DeviceExtension, &TargetVolumeName, FALSE, &TargetDeviceInformation);
if (!NT_SUCCESS(Status))
{
FreePool(SourceSymbolicName.Buffer);
return Status;
}
/* Notify if not disabled */
if (!TargetDeviceInformation->SkipNotifications)
{
PostOnlineNotification(DeviceExtension, &TargetDeviceInformation->SymbolicName);
}
/* Open the remote database */
RemoteDatabase = OpenRemoteDatabase(DeviceInformation, TRUE);
if (RemoteDatabase == 0)
{
FreePool(SourceSymbolicName.Buffer);
return STATUS_INSUFFICIENT_RESOURCES;
}
/* Browse all the entries */
Offset = 0;
Found = FALSE;
for (;;)
{
DatabaseEntry = GetRemoteDatabaseEntry(RemoteDatabase, Offset);
if (DatabaseEntry == NULL)
{
break;
}
/* Try to find ourselves */
DbName.MaximumLength = DatabaseEntry->SymbolicNameLength;
DbName.Length = DbName.MaximumLength;
DbName.Buffer = (PWSTR)((ULONG_PTR)DatabaseEntry + DatabaseEntry->SymbolicNameOffset);
if (RtlEqualUnicodeString(&TargetVolumeName, &DbName, TRUE))
{
/* Reference ourselves and update the entry */
++DatabaseEntry->EntryReferences;
Status = WriteRemoteDatabaseEntry(RemoteDatabase, Offset, DatabaseEntry);
FreePool(DatabaseEntry);
Found = TRUE;
break;
}
Offset += DatabaseEntry->EntrySize;
FreePool(DatabaseEntry);
}
/* We couldn't find ourselves, we'll have to add ourselves */
if (!Found)
{
ULONG EntrySize;
PUNIQUE_ID_REPLICATE UniqueIdReplicate;
/* Query the device unique ID */
Status = QueryDeviceInformation(&TargetVolumeName, NULL, &UniqueId, NULL, NULL, NULL, NULL, NULL);
if (!NT_SUCCESS(Status))
{
FreePool(SourceSymbolicName.Buffer);
CloseRemoteDatabase(RemoteDatabase);
return Status;
}
/* Allocate a database entry */
EntrySize = UniqueId->UniqueIdLength + TargetVolumeName.Length + sizeof(DATABASE_ENTRY);
DatabaseEntry = AllocatePool(EntrySize);
if (DatabaseEntry == NULL)
{
FreePool(UniqueId);
FreePool(SourceSymbolicName.Buffer);
CloseRemoteDatabase(RemoteDatabase);
return STATUS_INSUFFICIENT_RESOURCES;
}
/* Fill it in */
DatabaseEntry->EntrySize = EntrySize;
DatabaseEntry->EntryReferences = 1;
DatabaseEntry->SymbolicNameOffset = sizeof(DATABASE_ENTRY);
DatabaseEntry->SymbolicNameLength = TargetVolumeName.Length;
DatabaseEntry->UniqueIdOffset = TargetVolumeName.Length + sizeof(DATABASE_ENTRY);
DatabaseEntry->UniqueIdLength = UniqueId->UniqueIdLength;
RtlCopyMemory((PVOID)((ULONG_PTR)DatabaseEntry + sizeof(DATABASE_ENTRY)), TargetVolumeName.Buffer, DatabaseEntry->SymbolicNameLength);
RtlCopyMemory((PVOID)((ULONG_PTR)DatabaseEntry + DatabaseEntry->UniqueIdOffset), UniqueId->UniqueId, UniqueId->UniqueIdLength);
/* And write it down */
Status = AddRemoteDatabaseEntry(RemoteDatabase, DatabaseEntry);
FreePool(DatabaseEntry);
if (!NT_SUCCESS(Status))
{
FreePool(UniqueId);
FreePool(SourceSymbolicName.Buffer);
CloseRemoteDatabase(RemoteDatabase);
return Status;
}
/* And now, allocate an Unique ID item */
UniqueIdReplicate = AllocatePool(sizeof(UNIQUE_ID_REPLICATE));
if (UniqueIdReplicate == NULL)
{
FreePool(UniqueId);
FreePool(SourceSymbolicName.Buffer);
CloseRemoteDatabase(RemoteDatabase);
return Status;
}
/* To associate it with the device */
UniqueIdReplicate->UniqueId = UniqueId;
InsertTailList(&DeviceInformation->ReplicatedUniqueIdsListHead, &UniqueIdReplicate->ReplicatedUniqueIdsListEntry);
}
/* We're done with the remote database */
CloseRemoteDatabase(RemoteDatabase);
/* Check we were find writing the entry */
if (!NT_SUCCESS(Status))
{
FreePool(SourceSymbolicName.Buffer);
return Status;
}
/* This is the end, allocate an associated entry */
AssociatedEntry = AllocatePool(sizeof(ASSOCIATED_DEVICE_ENTRY));
if (AssociatedEntry == NULL)
{
FreePool(SourceSymbolicName.Buffer);
return STATUS_INSUFFICIENT_RESOURCES;
}
/* Initialize its source name string */
AssociatedEntry->String.Length = SourceSymbolicName.Length;
AssociatedEntry->String.MaximumLength = AssociatedEntry->String.Length + sizeof(UNICODE_NULL);
AssociatedEntry->String.Buffer = AllocatePool(AssociatedEntry->String.MaximumLength);
if (AssociatedEntry->String.Buffer == NULL)
{
FreePool(AssociatedEntry);
FreePool(SourceSymbolicName.Buffer);
return STATUS_INSUFFICIENT_RESOURCES;
}
/* Copy data & insert in list */
RtlCopyMemory(AssociatedEntry->String.Buffer, SourceSymbolicName.Buffer, SourceSymbolicName.Length);
AssociatedEntry->String.Buffer[SourceSymbolicName.Length / sizeof(WCHAR)] = UNICODE_NULL;
AssociatedEntry->DeviceInformation = DeviceInformation;
InsertTailList(&TargetDeviceInformation->AssociatedDevicesHead, &AssociatedEntry->AssociatedDevicesEntry);
/* We're done! */
FreePool(SourceSymbolicName.Buffer);
return STATUS_SUCCESS;
}
/*
* @implemented
*/
NTSTATUS
MountMgrVolumeMountPointDeleted(IN PDEVICE_EXTENSION DeviceExtension,
IN PIRP Irp,
IN NTSTATUS LockStatus)
{
LONG Offset;
NTSTATUS Status;
PLIST_ENTRY Entry;
HANDLE RemoteDatabase;
PDATABASE_ENTRY DatabaseEntry;
PUNIQUE_ID_REPLICATE UniqueIdReplicate;
PASSOCIATED_DEVICE_ENTRY AssociatedEntry;
PDEVICE_INFORMATION DeviceInformation, TargetDeviceInformation;
UNICODE_STRING LinkTarget, SourceDeviceName, SourceSymbolicName, TargetVolumeName, VolumeName, DbName;
/* Initialize string */
LinkTarget.Length = 0;
LinkTarget.MaximumLength = 0xC8;
LinkTarget.Buffer = AllocatePool(LinkTarget.MaximumLength);
if (LinkTarget.Buffer == NULL)
{
return STATUS_INSUFFICIENT_RESOURCES;
}
/* If the mount point was deleted, then, it changed!
* Also use it to query some information
*/
Status = MountMgrVolumeMountPointChanged(DeviceExtension, Irp, LockStatus, &SourceDeviceName, &SourceSymbolicName, &TargetVolumeName);
/* Pending means DB are under synchronization, bail out */
if (Status == STATUS_PENDING)
{
FreePool(LinkTarget.Buffer);
FreePool(SourceDeviceName.Buffer);
FreePool(SourceSymbolicName.Buffer);
return STATUS_SUCCESS;
}
else if (!NT_SUCCESS(Status))
{
FreePool(LinkTarget.Buffer);
return Status;
}
/* Query the device information */
Status = FindDeviceInfo(DeviceExtension, &SourceDeviceName, FALSE, &DeviceInformation);
if (!NT_SUCCESS(Status))
{
/* If it failed, first try to get volume name */
Status = QueryVolumeName(0, NULL, &SourceDeviceName, &LinkTarget, &VolumeName);
if (!NT_SUCCESS(Status))
{
/* Then, try to read the symlink */
Status = MountMgrQuerySymbolicLink(&SourceDeviceName, &LinkTarget);
if (!NT_SUCCESS(Status))
{
FreePool(LinkTarget.Buffer);
FreePool(SourceDeviceName.Buffer);
FreePool(SourceSymbolicName.Buffer);
return Status;
}
}
else
{
FreePool(VolumeName.Buffer);
}
FreePool(SourceDeviceName.Buffer);
SourceDeviceName.Length = LinkTarget.Length;
SourceDeviceName.MaximumLength = LinkTarget.MaximumLength;
SourceDeviceName.Buffer = LinkTarget.Buffer;
/* Now that we have the correct source, reattempt to query information */
Status = FindDeviceInfo(DeviceExtension, &SourceDeviceName, FALSE, &DeviceInformation);
if (!NT_SUCCESS(Status))
{
FreePool(SourceDeviceName.Buffer);
FreePool(SourceSymbolicName.Buffer);
return Status;
}
}
FreePool(SourceDeviceName.Buffer);
/* Get information about target device */
Status = FindDeviceInfo(DeviceExtension, &TargetVolumeName, FALSE, &TargetDeviceInformation);
if (!NT_SUCCESS(Status))
{
FreePool(SourceSymbolicName.Buffer);
return Status;
}
/* Open the remote database */
RemoteDatabase = OpenRemoteDatabase(DeviceInformation, TRUE);
if (RemoteDatabase == 0)
{
FreePool(SourceSymbolicName.Buffer);
return STATUS_INSUFFICIENT_RESOURCES;
}
/* Browse all the entries */
Offset = 0;
for (;;)
{
DatabaseEntry = GetRemoteDatabaseEntry(RemoteDatabase, Offset);
if (DatabaseEntry == NULL)
{
/* We didn't find ourselves, that's infortunate! */
FreePool(SourceSymbolicName.Buffer);
CloseRemoteDatabase(RemoteDatabase);
return STATUS_INVALID_PARAMETER;
}
/* Try to find ourselves */
DbName.MaximumLength = DatabaseEntry->SymbolicNameLength;
DbName.Length = DbName.MaximumLength;
DbName.Buffer = (PWSTR)((ULONG_PTR)DatabaseEntry + DatabaseEntry->SymbolicNameOffset);
if (RtlEqualUnicodeString(&TargetVolumeName, &DbName, TRUE))
{
break;
}
Offset += DatabaseEntry->EntrySize;
FreePool(DatabaseEntry);
}
/* Dereference ourselves */
DatabaseEntry->EntryReferences--;
if (DatabaseEntry->EntryReferences == 0)
{
/* If we're still referenced, just update the entry */
Status = WriteRemoteDatabaseEntry(RemoteDatabase, Offset, DatabaseEntry);
}
else
{
/* Otherwise, delete the entry */
Status = DeleteRemoteDatabaseEntry(RemoteDatabase, Offset);
if (!NT_SUCCESS(Status))
{
FreePool(DatabaseEntry);
FreePool(SourceSymbolicName.Buffer);
CloseRemoteDatabase(RemoteDatabase);
return Status;
}
/* Also, delete our unique ID replicated record */
for (Entry = DeviceInformation->ReplicatedUniqueIdsListHead.Flink;
Entry != &DeviceInformation->ReplicatedUniqueIdsListHead;
Entry = Entry->Flink)
{
UniqueIdReplicate = CONTAINING_RECORD(Entry, UNIQUE_ID_REPLICATE, ReplicatedUniqueIdsListEntry);
if (UniqueIdReplicate->UniqueId->UniqueIdLength == DatabaseEntry->UniqueIdLength &&
RtlCompareMemory(UniqueIdReplicate->UniqueId->UniqueId,
(PVOID)((ULONG_PTR)DatabaseEntry + DatabaseEntry->UniqueIdOffset),
DatabaseEntry->UniqueIdLength) == DatabaseEntry->UniqueIdLength)
{
break;
}
}
/* It has to exist! */
if (Entry == &DeviceInformation->ReplicatedUniqueIdsListHead)
{
FreePool(DatabaseEntry);
FreePool(SourceSymbolicName.Buffer);
CloseRemoteDatabase(RemoteDatabase);
return STATUS_UNSUCCESSFUL;
}
/* Remove it and free it */
RemoveEntryList(&UniqueIdReplicate->ReplicatedUniqueIdsListEntry);
FreePool(UniqueIdReplicate->UniqueId);
FreePool(UniqueIdReplicate);
}
/* We're done with the remote database */
FreePool(DatabaseEntry);
CloseRemoteDatabase(RemoteDatabase);
/* Check write operation succeed */
if (!NT_SUCCESS(Status))
{
FreePool(SourceSymbolicName.Buffer);
return Status;
}
/* Try to find our associated device entry */
for (Entry = TargetDeviceInformation->AssociatedDevicesHead.Flink;
Entry != &TargetDeviceInformation->AssociatedDevicesHead;
Entry = Entry->Flink)
{
AssociatedEntry = CONTAINING_RECORD(Entry, ASSOCIATED_DEVICE_ENTRY, AssociatedDevicesEntry);
/* If found, delete it */
if (AssociatedEntry->DeviceInformation == DeviceInformation &&
RtlEqualUnicodeString(&AssociatedEntry->String, &SourceSymbolicName, TRUE))
{
RemoveEntryList(&AssociatedEntry->AssociatedDevicesEntry);
FreePool(AssociatedEntry->String.Buffer);
FreePool(AssociatedEntry);
break;
}
}
/* We're done! */
FreePool(SourceSymbolicName.Buffer);
return STATUS_SUCCESS;
}
/*
* @implemented
*/
NTSTATUS
NTAPI
MountMgrDeviceControl(IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp)
{
PIO_STACK_LOCATION Stack;
NTSTATUS Status, LockStatus;
PDEVICE_EXTENSION DeviceExtension;
Stack = IoGetCurrentIrpStackLocation(Irp);
DeviceExtension = DeviceObject->DeviceExtension;
KeWaitForSingleObject(&(DeviceExtension->DeviceLock), Executive, KernelMode, FALSE, NULL);
switch (Stack->Parameters.DeviceIoControl.IoControlCode)
{
case IOCTL_MOUNTMGR_CREATE_POINT:
Status = MountMgrCreatePoint(DeviceExtension, Irp);
break;
case IOCTL_MOUNTMGR_DELETE_POINTS:
Status = MountMgrDeletePoints(DeviceExtension, Irp);
break;
case IOCTL_MOUNTMGR_QUERY_POINTS:
Status = MountMgrQueryPoints(DeviceExtension, Irp);
break;
case IOCTL_MOUNTMGR_DELETE_POINTS_DBONLY:
Status = MountMgrDeletePointsDbOnly(DeviceExtension, Irp);
break;
case IOCTL_MOUNTMGR_NEXT_DRIVE_LETTER:
Status = MountMgrNextDriveLetter(DeviceExtension, Irp);
break;
case IOCTL_MOUNTMGR_AUTO_DL_ASSIGNMENTS:
// NOTE: On Win7+, this is handled during driver re-initialization.
DeviceExtension->AutomaticDriveLetter = TRUE;
MountMgrAssignDriveLetters(DeviceExtension);
ReconcileAllDatabasesWithMaster(DeviceExtension);
WaitForOnlinesToComplete(DeviceExtension);
Status = STATUS_SUCCESS;
break;
case IOCTL_MOUNTMGR_VOLUME_MOUNT_POINT_CREATED:
KeReleaseSemaphore(&(DeviceExtension->DeviceLock), IO_NO_INCREMENT, 1, FALSE);
LockStatus = WaitForRemoteDatabaseSemaphore(DeviceExtension);
KeWaitForSingleObject(&(DeviceExtension->DeviceLock), Executive, KernelMode, FALSE, NULL);
Status = MountMgrVolumeMountPointCreated(DeviceExtension, Irp, LockStatus);
if (NT_SUCCESS(LockStatus))
{
ReleaseRemoteDatabaseSemaphore(DeviceExtension);
}
break;
case IOCTL_MOUNTMGR_VOLUME_MOUNT_POINT_DELETED:
KeReleaseSemaphore(&(DeviceExtension->DeviceLock), IO_NO_INCREMENT, 1, FALSE);
LockStatus = WaitForRemoteDatabaseSemaphore(DeviceExtension);
KeWaitForSingleObject(&(DeviceExtension->DeviceLock), Executive, KernelMode, FALSE, NULL);
Status = MountMgrVolumeMountPointDeleted(DeviceExtension, Irp, LockStatus);
if (NT_SUCCESS(LockStatus))
{
ReleaseRemoteDatabaseSemaphore(DeviceExtension);
}
break;
case IOCTL_MOUNTMGR_CHANGE_NOTIFY:
Status = MountMgrChangeNotify(DeviceExtension, Irp);
break;
case IOCTL_MOUNTMGR_KEEP_LINKS_WHEN_OFFLINE:
Status = MountMgrKeepLinksWhenOffline(DeviceExtension, Irp);
break;
case IOCTL_MOUNTMGR_CHECK_UNPROCESSED_VOLUMES:
Status = MountMgrCheckUnprocessedVolumes(DeviceExtension, Irp);
goto Complete;
case IOCTL_MOUNTMGR_VOLUME_ARRIVAL_NOTIFICATION:
KeReleaseSemaphore(&(DeviceExtension->DeviceLock), IO_NO_INCREMENT, 1, FALSE);
Status = MountMgrVolumeArrivalNotification(DeviceExtension, Irp);
goto Complete;
case IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATH:
Status = MountMgrQueryDosVolumePath(DeviceExtension, Irp);
break;
case IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATHS:
Status = MountMgrQueryDosVolumePaths(DeviceExtension, Irp);
break;
case IOCTL_MOUNTMGR_SCRUB_REGISTRY:
Status = MountMgrScrubRegistry(DeviceExtension);
break;
case IOCTL_MOUNTMGR_QUERY_AUTO_MOUNT:
Status = MountMgrQueryAutoMount(DeviceExtension, Irp);
break;
case IOCTL_MOUNTMGR_SET_AUTO_MOUNT:
Status = MountMgrSetAutoMount(DeviceExtension, Irp);
break;
default:
Status = STATUS_INVALID_DEVICE_REQUEST;
}
KeReleaseSemaphore(&(DeviceExtension->DeviceLock), IO_NO_INCREMENT, 1, FALSE);
if (Status != STATUS_PENDING)
{
goto Complete;
}
return Status;
Complete:
Irp->IoStatus.Status = Status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return Status;
}