winfsp/src/sys/mountdev.c
2021-05-21 15:57:05 -07:00

233 lines
8.2 KiB
C

/**
* @file sys/mountdev.c
*
* @copyright 2015-2021 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 <sys/driver.h>
NTSTATUS FspMountdevQueryDeviceName(
PDEVICE_OBJECT FsvrtDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp);
NTSTATUS FspMountdevQueryUniqueId(
PDEVICE_OBJECT FsvrtDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp);
BOOLEAN FspMountdevDeviceControl(
PDEVICE_OBJECT FsvrtDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp,
PNTSTATUS PResult);
NTSTATUS FspMountdevMake(
PDEVICE_OBJECT FsvrtDeviceObject, PDEVICE_OBJECT FsvolDeviceObject,
BOOLEAN Persistent);
VOID FspMountdevFini(
PDEVICE_OBJECT FsvrtDeviceObject);
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, FspMountdevQueryDeviceName)
#pragma alloc_text(PAGE, FspMountdevQueryUniqueId)
#pragma alloc_text(PAGE, FspMountdevDeviceControl)
#pragma alloc_text(PAGE, FspMountdevMake)
#pragma alloc_text(PAGE, FspMountdevFini)
#endif
NTSTATUS FspMountdevQueryDeviceName(
PDEVICE_OBJECT FsvrtDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp)
{
PAGED_CODE();
FSP_FSVRT_DEVICE_EXTENSION *FsvrtDeviceExtension = FspFsvrtDeviceExtension(FsvrtDeviceObject);
ULONG OutputBufferLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength;
PMOUNTDEV_NAME OutputBuffer = Irp->AssociatedIrp.SystemBuffer;
if (sizeof(MOUNTDEV_NAME) > OutputBufferLength)
/* NOTE: Windows storage samples also set: Irp->IoStatus.Information = sizeof(MOUNTDEV_NAME) */
return STATUS_BUFFER_TOO_SMALL;
RtlZeroMemory(OutputBuffer, sizeof(MOUNTDEV_NAME));
OutputBuffer->NameLength = FsvrtDeviceExtension->VolumeName.Length;
Irp->IoStatus.Information =
FIELD_OFFSET(MOUNTDEV_NAME, Name) + OutputBuffer->NameLength;
if (Irp->IoStatus.Information > OutputBufferLength)
{
Irp->IoStatus.Information = sizeof(MOUNTDEV_NAME);
return STATUS_BUFFER_OVERFLOW;
}
RtlCopyMemory(OutputBuffer->Name,
FsvrtDeviceExtension->VolumeName.Buffer, OutputBuffer->NameLength);
return STATUS_SUCCESS;
}
NTSTATUS FspMountdevQueryUniqueId(
PDEVICE_OBJECT FsvrtDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp)
{
PAGED_CODE();
FSP_FSVRT_DEVICE_EXTENSION *FsvrtDeviceExtension = FspFsvrtDeviceExtension(FsvrtDeviceObject);
ULONG OutputBufferLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength;
PMOUNTDEV_UNIQUE_ID OutputBuffer = Irp->AssociatedIrp.SystemBuffer;
if (sizeof(MOUNTDEV_UNIQUE_ID) > OutputBufferLength)
/* NOTE: Windows storage samples also set: Irp->IoStatus.Information = sizeof(MOUNTDEV_UNIQUE_ID) */
return STATUS_BUFFER_TOO_SMALL;
RtlZeroMemory(OutputBuffer, sizeof(MOUNTDEV_UNIQUE_ID));
OutputBuffer->UniqueIdLength = sizeof FsvrtDeviceExtension->UniqueId;
Irp->IoStatus.Information =
FIELD_OFFSET(MOUNTDEV_UNIQUE_ID, UniqueId) + OutputBuffer->UniqueIdLength;
if (Irp->IoStatus.Information > OutputBufferLength)
{
Irp->IoStatus.Information = sizeof(MOUNTDEV_UNIQUE_ID);
return STATUS_BUFFER_OVERFLOW;
}
RtlCopyMemory(OutputBuffer->UniqueId,
&FsvrtDeviceExtension->UniqueId, OutputBuffer->UniqueIdLength);
return STATUS_SUCCESS;
}
BOOLEAN FspMountdevDeviceControl(
PDEVICE_OBJECT FsvrtDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp,
PNTSTATUS PResult)
{
PAGED_CODE();
if (0 != FsvrtDeviceObject)
{
FSP_FSVRT_DEVICE_EXTENSION *FsvrtDeviceExtension = FspFsvrtDeviceExtension(FsvrtDeviceObject);
if (0 != InterlockedCompareExchange(&FsvrtDeviceExtension->IsMountdev, 0, 0))
{
switch (IrpSp->Parameters.DeviceIoControl.IoControlCode)
{
case IOCTL_MOUNTDEV_QUERY_DEVICE_NAME:
*PResult = FspMountdevQueryDeviceName(FsvrtDeviceObject, Irp, IrpSp);
return TRUE;
case IOCTL_MOUNTDEV_QUERY_UNIQUE_ID:
*PResult = FspMountdevQueryUniqueId(FsvrtDeviceObject, Irp, IrpSp);
return TRUE;
}
}
}
return FALSE;
}
NTSTATUS FspMountdevMake(
PDEVICE_OBJECT FsvrtDeviceObject, PDEVICE_OBJECT FsvolDeviceObject,
BOOLEAN Persistent)
{
/*
* This function converts the fsvrt device into a mountdev device that
* responds to MountManager IOCTL's. This allows the fsvrt device to
* be mounted using the MountManager.
*
* This function requires protection against concurrency. In general this
* is achieved by acquiring the GlobalDeviceLock.
*/
PAGED_CODE();
FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(FsvolDeviceObject);
FSP_FSVRT_DEVICE_EXTENSION *FsvrtDeviceExtension = FspFsvrtDeviceExtension(FsvrtDeviceObject);
UNICODE_STRING String;
WCHAR StringBuf[FSP_FSCTL_VOLUME_FSNAME_SIZE / sizeof(WCHAR) + 18];
GUID Guid;
NTSTATUS Result;
ASSERT(FsvolDeviceExtension->FsvrtDeviceObject == FsvrtDeviceObject);
if (0 != InterlockedCompareExchange(&FsvrtDeviceExtension->IsMountdev, 0, 0))
return Persistent == FsvrtDeviceExtension->Persistent ?
STATUS_TOO_LATE : STATUS_ACCESS_DENIED;
FsvrtDeviceExtension->Persistent = Persistent;
if (Persistent)
{
/* make UUID v5 from the fsvrt device GUID and a unique string derived from VolumeParams */
RtlInitEmptyUnicodeString(&String, StringBuf, sizeof StringBuf);
Result = RtlUnicodeStringPrintf(&String,
L"%s:%08lx:%08lx",
FsvolDeviceExtension->VolumeParams.FileSystemName,
FsvolDeviceExtension->VolumeParams.VolumeSerialNumber,
FsvolDeviceExtension->VolumeParams.VolumeCreationTime);
ASSERT(NT_SUCCESS(Result));
Result = FspUuid5Make(&FspFsvrtDeviceClassGuid, String.Buffer, String.Length, &Guid);
}
else
/* create volume guid */
Result = FspCreateGuid(&Guid);
if (!NT_SUCCESS(Result))
goto exit;
/* initialize the fsvrt device extension */
RtlCopyMemory(&FsvrtDeviceExtension->UniqueId, &Guid, sizeof Guid);
RtlInitEmptyUnicodeString(&FsvrtDeviceExtension->VolumeName,
FsvrtDeviceExtension->VolumeNameBuf, sizeof FsvrtDeviceExtension->VolumeNameBuf);
RtlCopyUnicodeString(&FsvrtDeviceExtension->VolumeName, &FsvolDeviceExtension->VolumeName);
/* mark the fsvrt device as initialized */
InterlockedIncrement(&FspFsvrtDeviceExtension(FsvrtDeviceObject)->IsMountdev);
Result = STATUS_SUCCESS;
exit:
return Result;
}
VOID FspMountdevFini(
PDEVICE_OBJECT FsvrtDeviceObject)
{
PAGED_CODE();
FSP_FSVRT_DEVICE_EXTENSION *FsvrtDeviceExtension = FspFsvrtDeviceExtension(FsvrtDeviceObject);
PVOID Buffer = 0;
ULONG Length = 4096;
MOUNTMGR_MOUNT_POINT *MountPoint;
NTSTATUS Result;
if (0 == InterlockedCompareExchange(&FsvrtDeviceExtension->IsMountdev, 0, 0))
return;
if (FsvrtDeviceExtension->Persistent)
/* if the mountdev is marked as persistent do not purge the MountManager */
return;
Buffer = FspAllocNonPaged(Length);
if (0 == Buffer)
{
Result = STATUS_INSUFFICIENT_RESOURCES;
goto exit;
}
MountPoint = Buffer;
RtlZeroMemory(MountPoint, sizeof *MountPoint);
MountPoint->UniqueIdOffset = sizeof(MOUNTMGR_MOUNT_POINT);
MountPoint->UniqueIdLength = sizeof FsvrtDeviceExtension->UniqueId;
RtlCopyMemory((PUINT8)MountPoint + MountPoint->UniqueIdOffset,
&FsvrtDeviceExtension->UniqueId, MountPoint->UniqueIdLength);
Result = FspSendMountmgrDeviceControlIrp(IOCTL_MOUNTMGR_DELETE_POINTS,
Buffer, MountPoint->UniqueIdOffset + MountPoint->UniqueIdLength, &Length);
exit:
if (0 != Buffer)
FspFree(Buffer);
}