winfsp/src/shared/ku/mountmgr.c
2024-01-08 12:38:44 +00:00

600 lines
18 KiB
C

/**
* @file shared/ku/mountmgr.c
*
* @copyright 2015-2024 Bill Zissimopoulos
*/
/*
* This file is part of WinFsp.
*
* You can redistribute it and/or modify it under the terms of the GNU
* General Public License version 3 as published by the Free Software
* Foundation.
*
* Licensees holding a valid commercial license may use this software
* in accordance with the commercial license agreement provided in
* conjunction with the software. The terms and conditions of any such
* commercial license agreement shall govern, supersede, and render
* ineffective any application of the GPLv3 license to this software,
* notwithstanding of any reference thereto in the software or
* associated repository.
*/
#include <shared/ku/library.h>
#pragma warning(push)
#pragma warning(disable:4459) /* declaration of 'identifier' hides global declaration */
static NTSTATUS FspMountmgrControl(ULONG IoControlCode,
PVOID InputBuffer, ULONG InputBufferLength, PVOID OutputBuffer, PULONG POutputBufferLength);
static NTSTATUS FspMountmgrNotifyVolumeArrival(
PUNICODE_STRING VolumeName, GUID *UniqueId);
static NTSTATUS FspMountmgrNotifyMountPoint(
PUNICODE_STRING VolumeName, PUNICODE_STRING MountPoint, BOOLEAN Created);
static NTSTATUS FspMountmgrCreateMountPoint(
PUNICODE_STRING VolumeName, PUNICODE_STRING MountPoint);
static NTSTATUS FspMountmgrDeleteMountPoint(
PUNICODE_STRING MountPoint);
static VOID FspMountmgrDeleteRegistry(
GUID *UniqueId);
NTSTATUS FspMountmgrCreateDrive(
PUNICODE_STRING VolumeName, GUID *UniqueId, PUNICODE_STRING MountPoint);
NTSTATUS FspMountmgrDeleteDrive(
PUNICODE_STRING MountPoint);
NTSTATUS FspMountmgrNotifyCreateDirectory(
PUNICODE_STRING VolumeName, GUID *UniqueId, PUNICODE_STRING MountPoint);
NTSTATUS FspMountmgrNotifyDeleteDirectory(
PUNICODE_STRING VolumeName, PUNICODE_STRING MountPoint);
#if defined(_KERNEL_MODE)
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, FspMountmgrControl)
#pragma alloc_text(PAGE, FspMountmgrNotifyVolumeArrival)
#pragma alloc_text(PAGE, FspMountmgrNotifyMountPoint)
#pragma alloc_text(PAGE, FspMountmgrCreateMountPoint)
#pragma alloc_text(PAGE, FspMountmgrDeleteMountPoint)
#pragma alloc_text(PAGE, FspMountmgrCreateDrive)
#pragma alloc_text(PAGE, FspMountmgrDeleteDrive)
#pragma alloc_text(PAGE, FspMountmgrNotifyCreateDirectory)
#pragma alloc_text(PAGE, FspMountmgrNotifyDeleteDirectory)
#endif
#endif
static NTSTATUS FspMountmgrControl(ULONG IoControlCode,
PVOID InputBuffer, ULONG InputBufferLength, PVOID OutputBuffer, PULONG POutputBufferLength)
{
#if defined(_KERNEL_MODE)
FSP_KU_CODE;
UNICODE_STRING DeviceName;
OBJECT_ATTRIBUTES ObjectAttributes;
IO_STATUS_BLOCK IoStatus;
HANDLE MgrHandle = 0;
ULONG Bytes = 0;
if (0 == POutputBufferLength)
POutputBufferLength = &Bytes;
RtlInitUnicodeString(&DeviceName, L"\\Device\\MountPointManager");
InitializeObjectAttributes(
&ObjectAttributes,
&DeviceName,
OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE,
0/*RootDirectory*/,
0);
IoStatus.Status = ZwOpenFile(
&MgrHandle,
GENERIC_READ | GENERIC_WRITE,
&ObjectAttributes,
&IoStatus,
FILE_SHARE_READ | FILE_SHARE_WRITE,
FILE_SYNCHRONOUS_IO_ALERT);
if (!NT_SUCCESS(IoStatus.Status))
goto exit;
IoStatus.Status = ZwDeviceIoControlFile(
MgrHandle,
0, 0, 0,
&IoStatus,
IoControlCode,
InputBuffer, InputBufferLength, OutputBuffer, *POutputBufferLength);
if (!NT_SUCCESS(IoStatus.Status))
goto exit;
*POutputBufferLength = (ULONG)IoStatus.Information;
IoStatus.Status = STATUS_SUCCESS;
exit:
if (0 != MgrHandle)
ZwClose(MgrHandle);
return IoStatus.Status;
#else
HANDLE MgrHandle = INVALID_HANDLE_VALUE;
DWORD Bytes = 0;
NTSTATUS Result;
if (0 == POutputBufferLength)
POutputBufferLength = &Bytes;
MgrHandle = CreateFileW(L"\\\\.\\MountPointManager",
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
0,
OPEN_EXISTING,
0,
0);
if (INVALID_HANDLE_VALUE == MgrHandle)
{
Result = FspNtStatusFromWin32(GetLastError());
goto exit;
}
if (!DeviceIoControl(MgrHandle,
IoControlCode,
InputBuffer, InputBufferLength, OutputBuffer, *POutputBufferLength,
&Bytes, 0))
{
Result = FspNtStatusFromWin32(GetLastError());
goto exit;
}
*POutputBufferLength = Bytes;
Result = STATUS_SUCCESS;
exit:
if (INVALID_HANDLE_VALUE != MgrHandle)
CloseHandle(MgrHandle);
return Result;
#endif
}
static NTSTATUS FspMountmgrNotifyVolumeArrival(
PUNICODE_STRING VolumeName, GUID *UniqueId)
{
FSP_KU_CODE;
/* mountmgr.h */
typedef enum
{
Disabled = 0,
Enabled,
} MOUNTMGR_AUTO_MOUNT_STATE;
typedef struct
{
MOUNTMGR_AUTO_MOUNT_STATE CurrentState;
} MOUNTMGR_QUERY_AUTO_MOUNT;
typedef struct
{
MOUNTMGR_AUTO_MOUNT_STATE NewState;
} MOUNTMGR_SET_AUTO_MOUNT;
typedef struct
{
USHORT DeviceNameLength;
WCHAR DeviceName[1];
} MOUNTMGR_TARGET_NAME;
MOUNTMGR_QUERY_AUTO_MOUNT QueryAutoMount;
MOUNTMGR_SET_AUTO_MOUNT SetAutoMount;
MOUNTMGR_TARGET_NAME *TargetName = 0;
ULONG QueryAutoMountSize, TargetNameSize;
NTSTATUS Result;
QueryAutoMountSize = sizeof QueryAutoMount;
TargetNameSize = FIELD_OFFSET(MOUNTMGR_TARGET_NAME, DeviceName) + VolumeName->Length;
TargetName = MemAlloc(TargetNameSize);
if (0 == TargetName)
{
Result = STATUS_INSUFFICIENT_RESOURCES;
goto exit;
}
/* query the current AutoMount value and save it */
Result = FspMountmgrControl(
CTL_CODE('m', 15, METHOD_BUFFERED, FILE_ANY_ACCESS),
/* IOCTL_MOUNTMGR_QUERY_AUTO_MOUNT */
0, 0, &QueryAutoMount, &QueryAutoMountSize);
if (!NT_SUCCESS(Result))
goto exit;
/* disable AutoMount */
SetAutoMount.NewState = 0;
Result = FspMountmgrControl(
CTL_CODE('m', 16, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS),
/* IOCTL_MOUNTMGR_SET_AUTO_MOUNT */
&SetAutoMount, sizeof SetAutoMount, 0, 0);
if (!NT_SUCCESS(Result))
goto exit;
/* announce volume arrival */
memset(TargetName, 0, sizeof *TargetName);
TargetName->DeviceNameLength = VolumeName->Length;
memcpy(TargetName->DeviceName, VolumeName->Buffer, VolumeName->Length);
Result = FspMountmgrControl(
CTL_CODE('m', 11, METHOD_BUFFERED, FILE_READ_ACCESS),
/* IOCTL_MOUNTMGR_VOLUME_ARRIVAL_NOTIFICATION */
TargetName, TargetNameSize, 0, 0);
if (!NT_SUCCESS(Result))
goto exit;
/* reset the AutoMount value to the saved one */
SetAutoMount.NewState = QueryAutoMount.CurrentState;
FspMountmgrControl(
CTL_CODE('m', 16, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS),
/* IOCTL_MOUNTMGR_SET_AUTO_MOUNT */
&SetAutoMount, sizeof SetAutoMount, 0, 0);
Result = STATUS_SUCCESS;
exit:
MemFree(TargetName);
return Result;
}
static NTSTATUS FspMountmgrNotifyMountPoint(
PUNICODE_STRING VolumeName, PUNICODE_STRING MountPoint, BOOLEAN Created)
{
FSP_KU_CODE;
/* mountmgr.h */
typedef struct
{
USHORT SourceVolumeNameOffset;
USHORT SourceVolumeNameLength;
USHORT TargetVolumeNameOffset;
USHORT TargetVolumeNameLength;
} MOUNTMGR_VOLUME_MOUNT_POINT;
MOUNTMGR_VOLUME_MOUNT_POINT *VolumeMountPoint = 0;
ULONG VolumeMountPointSize;
NTSTATUS Result;
VolumeMountPointSize = sizeof *VolumeMountPoint +
sizeof L"\\DosDevices\\" - sizeof(WCHAR) + MountPoint->Length + VolumeName->Length;
VolumeMountPoint = MemAlloc(VolumeMountPointSize);
if (0 == VolumeMountPoint)
{
Result = STATUS_INSUFFICIENT_RESOURCES;
goto exit;
}
/* notify volume mount point created/deleted */
memset(VolumeMountPoint, 0, sizeof *VolumeMountPoint);
VolumeMountPoint->SourceVolumeNameOffset = sizeof *VolumeMountPoint;
VolumeMountPoint->SourceVolumeNameLength = (USHORT)(
sizeof L"\\DosDevices\\" - sizeof(WCHAR) + MountPoint->Length);
VolumeMountPoint->TargetVolumeNameOffset =
VolumeMountPoint->SourceVolumeNameOffset + VolumeMountPoint->SourceVolumeNameLength;
VolumeMountPoint->TargetVolumeNameLength = VolumeName->Length;
memcpy((PUINT8)VolumeMountPoint + VolumeMountPoint->SourceVolumeNameOffset,
L"\\DosDevices\\", sizeof L"\\DosDevices\\" - sizeof(WCHAR));
memcpy((PUINT8)VolumeMountPoint +
VolumeMountPoint->SourceVolumeNameOffset + (sizeof L"\\DosDevices\\" - sizeof(WCHAR)),
MountPoint->Buffer, MountPoint->Length);
memcpy((PUINT8)VolumeMountPoint + VolumeMountPoint->TargetVolumeNameOffset,
VolumeName->Buffer, VolumeName->Length);
Result = FspMountmgrControl(
Created ?
CTL_CODE('m', 6, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) :
/* IOCTL_MOUNTMGR_VOLUME_MOUNT_POINT_CREATED */
CTL_CODE('m', 7, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS),
/* IOCTL_MOUNTMGR_VOLUME_MOUNT_POINT_DELETED */
VolumeMountPoint, VolumeMountPointSize, 0, 0);
if (!NT_SUCCESS(Result))
goto exit;
Result = STATUS_SUCCESS;
exit:
MemFree(VolumeMountPoint);
return Result;
}
static NTSTATUS FspMountmgrCreateMountPoint(
PUNICODE_STRING VolumeName, PUNICODE_STRING MountPoint)
{
FSP_KU_CODE;
/* mountmgr.h */
typedef struct
{
USHORT SymbolicLinkNameOffset;
USHORT SymbolicLinkNameLength;
USHORT DeviceNameOffset;
USHORT DeviceNameLength;
} MOUNTMGR_CREATE_POINT_INPUT;
MOUNTMGR_CREATE_POINT_INPUT *CreatePointInput = 0;
ULONG CreatePointInputSize;
NTSTATUS Result;
CreatePointInputSize = sizeof *CreatePointInput +
sizeof L"\\DosDevices\\X:" - sizeof(WCHAR) + VolumeName->Length;
CreatePointInput = MemAlloc(CreatePointInputSize);
if (0 == CreatePointInput)
{
Result = STATUS_INSUFFICIENT_RESOURCES;
goto exit;
}
/* create mount point */
memset(CreatePointInput, 0, sizeof *CreatePointInput);
CreatePointInput->SymbolicLinkNameOffset = sizeof *CreatePointInput;
CreatePointInput->SymbolicLinkNameLength = sizeof L"\\DosDevices\\X:" - sizeof(WCHAR);
CreatePointInput->DeviceNameOffset =
CreatePointInput->SymbolicLinkNameOffset + CreatePointInput->SymbolicLinkNameLength;
CreatePointInput->DeviceNameLength = VolumeName->Length;
memcpy((PUINT8)CreatePointInput + CreatePointInput->SymbolicLinkNameOffset,
L"\\DosDevices\\X:", CreatePointInput->SymbolicLinkNameLength);
((PWCHAR)((PUINT8)CreatePointInput + CreatePointInput->SymbolicLinkNameOffset))[12] =
MountPoint->Buffer[0] & ~0x20;
/* convert to uppercase */
memcpy((PUINT8)CreatePointInput + CreatePointInput->DeviceNameOffset,
VolumeName->Buffer, VolumeName->Length);
Result = FspMountmgrControl(
CTL_CODE('m', 0, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS),
/* IOCTL_MOUNTMGR_CREATE_POINT */
CreatePointInput, CreatePointInputSize, 0, 0);
if (!NT_SUCCESS(Result))
goto exit;
Result = STATUS_SUCCESS;
exit:
MemFree(CreatePointInput);
return Result;
}
static NTSTATUS FspMountmgrDeleteMountPoint(
PUNICODE_STRING MountPoint)
{
FSP_KU_CODE;
/* mountmgr.h */
typedef struct
{
ULONG SymbolicLinkNameOffset;
USHORT SymbolicLinkNameLength;
USHORT Reserved1;
ULONG UniqueIdOffset;
USHORT UniqueIdLength;
USHORT Reserved2;
ULONG DeviceNameOffset;
USHORT DeviceNameLength;
USHORT Reserved3;
} MOUNTMGR_MOUNT_POINT;
typedef struct
{
ULONG Size;
ULONG NumberOfMountPoints;
MOUNTMGR_MOUNT_POINT MountPoints[1];
} MOUNTMGR_MOUNT_POINTS;
MOUNTMGR_MOUNT_POINT *Input = 0;
MOUNTMGR_MOUNT_POINTS *Output = 0;
ULONG InputSize, OutputSize;
NTSTATUS Result;
InputSize = sizeof *Input + sizeof L"\\DosDevices\\X:" - sizeof(WCHAR);
OutputSize = 4096;
Input = MemAlloc(InputSize);
if (0 == Input)
{
Result = STATUS_INSUFFICIENT_RESOURCES;
goto exit;
}
Output = MemAlloc(OutputSize);
if (0 == Output)
{
Result = STATUS_INSUFFICIENT_RESOURCES;
goto exit;
}
/* delete mount point */
memset(Input, 0, sizeof *Input);
Input->SymbolicLinkNameOffset = sizeof *Input;
Input->SymbolicLinkNameLength = sizeof L"\\DosDevices\\X:" - sizeof(WCHAR);
memcpy((PUINT8)Input + Input->SymbolicLinkNameOffset,
L"\\DosDevices\\X:", Input->SymbolicLinkNameLength);
((PWCHAR)((PUINT8)Input + Input->SymbolicLinkNameOffset))[12] =
MountPoint->Buffer[0] & ~0x20;
/* convert to uppercase */
Result = FspMountmgrControl(
CTL_CODE('m', 1, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS),
/* IOCTL_MOUNTMGR_DELETE_POINTS */
Input, InputSize, Output, &OutputSize);
if (!NT_SUCCESS(Result))
goto exit;
Result = STATUS_SUCCESS;
exit:
MemFree(Output);
MemFree(Input);
return Result;
}
static VOID FspMountmgrDeleteRegistry(GUID *UniqueId)
{
#if defined(_KERNEL_MODE)
FSP_KU_CODE;
UNICODE_STRING Path;
OBJECT_ATTRIBUTES ObjectAttributes;
HANDLE Handle = 0;
union
{
KEY_VALUE_FULL_INFORMATION V;
UINT8 B[FIELD_OFFSET(KEY_VALUE_FULL_INFORMATION, Name) + 255 + sizeof *UniqueId];
} FullInformation;
ULONG FullInformationLength;
UNICODE_STRING ValueName;
NTSTATUS Result;
RtlInitUnicodeString(&Path, L"\\Registry\\Machine\\System\\MountedDevices");
InitializeObjectAttributes(
&ObjectAttributes,
&Path,
OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE,
0/*RootDirectory*/,
0);
Result = ZwOpenKey(&Handle, KEY_QUERY_VALUE, &ObjectAttributes);
if (NT_SUCCESS(Result))
{
for (ULONG I = 0;; I++)
{
Result = ZwEnumerateValueKey(Handle,
I, KeyValueFullInformation, &FullInformation,
sizeof FullInformation, &FullInformationLength);
if (STATUS_NO_MORE_ENTRIES == Result)
break;
else if (!NT_SUCCESS(Result))
continue;
if (REG_BINARY == FullInformation.V.Type &&
sizeof *UniqueId == FullInformation.V.DataLength &&
InlineIsEqualGUID((GUID *)((PUINT8)&FullInformation.V + FullInformation.V.DataOffset),
UniqueId))
{
ValueName.Length = ValueName.MaximumLength = (USHORT)FullInformation.V.NameLength;
ValueName.Buffer = FullInformation.V.Name;
Result = ZwDeleteValueKey(Handle, &ValueName);
if (NT_SUCCESS(Result))
/* reset index after modifying key; only safe way to use RegEnumValueW with modifications */
I = (ULONG)-1;
}
}
ZwClose(Handle);
}
#else
HKEY RegKey;
LONG RegResult;
WCHAR RegValueName[MAX_PATH];
UINT8 RegValueData[sizeof *UniqueId];
DWORD RegValueNameSize, RegValueDataSize;
DWORD RegType;
RegResult = RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"System\\MountedDevices",
0, KEY_READ | KEY_WRITE, &RegKey);
if (ERROR_SUCCESS == RegResult)
{
for (DWORD I = 0;; I++)
{
RegValueNameSize = MAX_PATH;
RegValueDataSize = sizeof RegValueData;
RegResult = RegEnumValueW(RegKey,
I, RegValueName, &RegValueNameSize, 0, &RegType, RegValueData, &RegValueDataSize);
if (ERROR_NO_MORE_ITEMS == RegResult)
break;
else if (ERROR_SUCCESS != RegResult)
continue;
if (REG_BINARY == RegType &&
sizeof RegValueData == RegValueDataSize &&
InlineIsEqualGUID((GUID *)&RegValueData, UniqueId))
{
RegResult = RegDeleteValueW(RegKey, RegValueName);
if (ERROR_SUCCESS == RegResult)
/* reset index after modifying key; only safe way to use RegEnumValueW with modifications */
I = -1;
}
}
RegCloseKey(RegKey);
}
#endif
}
NTSTATUS FspMountmgrCreateDrive(
PUNICODE_STRING VolumeName, GUID *UniqueId, PUNICODE_STRING MountPoint)
{
FSP_KU_CODE;
NTSTATUS Result;
/* notify the MountManager about the new volume */
Result = FspMountmgrNotifyVolumeArrival(VolumeName, UniqueId);
if (!NT_SUCCESS(Result))
goto exit;
/* use the MountManager to create the drive */
Result = FspMountmgrCreateMountPoint(VolumeName, MountPoint);
if (!NT_SUCCESS(Result))
goto exit;
/* HACK: delete the MountManager registry entries */
FspMountmgrDeleteRegistry(UniqueId);
Result = STATUS_SUCCESS;
exit:
return Result;
}
NTSTATUS FspMountmgrDeleteDrive(
PUNICODE_STRING MountPoint)
{
FSP_KU_CODE;
NTSTATUS Result;
/* use the MountManager to delete the drive */
Result = FspMountmgrDeleteMountPoint(MountPoint);
if (!NT_SUCCESS(Result))
goto exit;
Result = STATUS_SUCCESS;
exit:
return Result;
}
NTSTATUS FspMountmgrNotifyCreateDirectory(
PUNICODE_STRING VolumeName, GUID *UniqueId, PUNICODE_STRING MountPoint)
{
FSP_KU_CODE;
NTSTATUS Result;
/* notify the MountManager about the new volume */
Result = FspMountmgrNotifyVolumeArrival(VolumeName, UniqueId);
if (!NT_SUCCESS(Result))
goto exit;
/* notify the MountManager about the created directory mount point */
Result = FspMountmgrNotifyMountPoint(VolumeName, MountPoint, TRUE);
if (!NT_SUCCESS(Result))
goto exit;
/* HACK: delete the MountManager registry entries */
FspMountmgrDeleteRegistry(UniqueId);
Result = STATUS_SUCCESS;
exit:
return Result;
}
NTSTATUS FspMountmgrNotifyDeleteDirectory(
PUNICODE_STRING VolumeName, PUNICODE_STRING MountPoint)
{
FSP_KU_CODE;
/* notify the MountManager about the deleted directory mount point */
return FspMountmgrNotifyMountPoint(VolumeName, MountPoint, FALSE);
}
#pragma warning(pop)