mirror of
https://github.com/winfsp/winfsp.git
synced 2025-07-03 09:22:57 -05:00
sys: fsmup device
- This commit introduces the fsmup device, which is a major change in how network file systems are handled. Previously every network file system's fsvol device was directly registered with the MUP. Now there is a single fsmup device that is registered with the MUP; network file systems' fsvol devices register with fsmup instead. The fsmup device maintains a prefix table which it uses to demultiplex and forward requests to the appropriate fsvol device. - This device change was necessatitated to fix issue #87.
This commit is contained in:
266
src/sys/mup.c
Normal file
266
src/sys/mup.c
Normal file
@ -0,0 +1,266 @@
|
||||
/**
|
||||
* @file sys/mup.c
|
||||
*
|
||||
* @copyright 2015-2018 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 file in
|
||||
* accordance with the commercial license agreement provided with the
|
||||
* software.
|
||||
*/
|
||||
|
||||
#include <sys/driver.h>
|
||||
|
||||
BOOLEAN FspMupRegister(
|
||||
PDEVICE_OBJECT FsmupDeviceObject, PDEVICE_OBJECT FsvolDeviceObject);
|
||||
VOID FspMupUnregister(
|
||||
PDEVICE_OBJECT FsmupDeviceObject, PDEVICE_OBJECT FsvolDeviceObject);
|
||||
NTSTATUS FspMupHandleIrp(
|
||||
PDEVICE_OBJECT FsmupDeviceObject, PIRP Irp);
|
||||
static NTSTATUS FspMupRedirQueryPathEx(
|
||||
PDEVICE_OBJECT FsmupDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp);
|
||||
|
||||
#ifdef ALLOC_PRAGMA
|
||||
#pragma alloc_text(PAGE, FspMupRegister)
|
||||
#pragma alloc_text(PAGE, FspMupUnregister)
|
||||
#pragma alloc_text(PAGE, FspMupHandleIrp)
|
||||
#pragma alloc_text(PAGE, FspMupRedirQueryPathEx)
|
||||
#endif
|
||||
|
||||
BOOLEAN FspMupRegister(
|
||||
PDEVICE_OBJECT FsmupDeviceObject, PDEVICE_OBJECT FsvolDeviceObject)
|
||||
{
|
||||
PAGED_CODE();
|
||||
|
||||
BOOLEAN Result;
|
||||
FSP_FSMUP_DEVICE_EXTENSION *FsmupDeviceExtension = FspFsmupDeviceExtension(FsmupDeviceObject);
|
||||
FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(FsvolDeviceObject);
|
||||
|
||||
ExAcquireResourceExclusiveLite(&FsmupDeviceExtension->PrefixTableResource, TRUE);
|
||||
Result = RtlInsertUnicodePrefix(&FsmupDeviceExtension->PrefixTable,
|
||||
&FsvolDeviceExtension->VolumePrefix, &FsvolDeviceExtension->VolumePrefixEntry);
|
||||
if (Result)
|
||||
FspDeviceReference(FsvolDeviceObject);
|
||||
ExReleaseResourceLite(&FsmupDeviceExtension->PrefixTableResource);
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
VOID FspMupUnregister(
|
||||
PDEVICE_OBJECT FsmupDeviceObject, PDEVICE_OBJECT FsvolDeviceObject)
|
||||
{
|
||||
PAGED_CODE();
|
||||
|
||||
FSP_FSMUP_DEVICE_EXTENSION *FsmupDeviceExtension = FspFsmupDeviceExtension(FsmupDeviceObject);
|
||||
FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(FsvolDeviceObject);
|
||||
|
||||
ExAcquireResourceExclusiveLite(&FsmupDeviceExtension->PrefixTableResource, TRUE);
|
||||
RtlRemoveUnicodePrefix(&FsmupDeviceExtension->PrefixTable,
|
||||
&FsvolDeviceExtension->VolumePrefixEntry);
|
||||
FspDeviceDereference(FsvolDeviceObject);
|
||||
ExReleaseResourceLite(&FsmupDeviceExtension->PrefixTableResource);
|
||||
}
|
||||
|
||||
NTSTATUS FspMupHandleIrp(
|
||||
PDEVICE_OBJECT FsmupDeviceObject, PIRP Irp)
|
||||
{
|
||||
PAGED_CODE();
|
||||
|
||||
FSP_FSMUP_DEVICE_EXTENSION *FsmupDeviceExtension = FspFsmupDeviceExtension(FsmupDeviceObject);
|
||||
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
|
||||
PFILE_OBJECT FileObject = IrpSp->FileObject;
|
||||
PDEVICE_OBJECT FsvolDeviceObject = 0;
|
||||
PUNICODE_PREFIX_TABLE_ENTRY Entry;
|
||||
BOOLEAN DeviceDeref = FALSE;
|
||||
NTSTATUS Result;
|
||||
|
||||
FsRtlEnterFileSystem();
|
||||
|
||||
switch (IrpSp->MajorFunction)
|
||||
{
|
||||
case IRP_MJ_CREATE:
|
||||
/*
|
||||
* A CREATE request with an empty file name indicates that the fsmup device
|
||||
* is being opened. Check for this case and handle it.
|
||||
*/
|
||||
if (0 == FileObject->FileName.Length)
|
||||
{
|
||||
Irp->IoStatus.Status = STATUS_SUCCESS;
|
||||
Irp->IoStatus.Information = FILE_OPENED;
|
||||
IoCompleteRequest(Irp, FSP_IO_INCREMENT);
|
||||
Result = Irp->IoStatus.Status;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/*
|
||||
* Every other CREATE request must be forwarded to the appropriate fsvol device.
|
||||
*/
|
||||
|
||||
if (0 != FileObject->RelatedFileObject)
|
||||
FileObject = FileObject->RelatedFileObject;
|
||||
|
||||
ExAcquireResourceExclusiveLite(&FsmupDeviceExtension->PrefixTableResource, TRUE);
|
||||
Entry = RtlFindUnicodePrefix(&FsmupDeviceExtension->PrefixTable,
|
||||
&FileObject->FileName, 0);
|
||||
if (0 != Entry)
|
||||
{
|
||||
FsvolDeviceObject = CONTAINING_RECORD(Entry,
|
||||
FSP_FSVOL_DEVICE_EXTENSION, VolumePrefixEntry)->FsvolDeviceObject;
|
||||
FspDeviceReference(FsvolDeviceObject);
|
||||
DeviceDeref = TRUE;
|
||||
}
|
||||
ExReleaseResourceLite(&FsmupDeviceExtension->PrefixTableResource);
|
||||
break;
|
||||
|
||||
case IRP_MJ_DEVICE_CONTROL:
|
||||
/*
|
||||
* A DEVICE_CONTROL request with IOCTL_REDIR_QUERY_PATH_EX must be handled
|
||||
* by the fsmup device. Check for this case and handle it.
|
||||
*/
|
||||
if (IOCTL_REDIR_QUERY_PATH_EX == IrpSp->Parameters.DeviceIoControl.IoControlCode)
|
||||
{
|
||||
Irp->IoStatus.Status = FspMupRedirQueryPathEx(FsmupDeviceObject, Irp, IrpSp);
|
||||
IoCompleteRequest(Irp, FSP_IO_INCREMENT);
|
||||
Result = Irp->IoStatus.Status;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/*
|
||||
* Every other DEVICE_CONTROL request must be forwarded to the appropriate fsvol device.
|
||||
*/
|
||||
|
||||
/* fall through! */
|
||||
|
||||
default:
|
||||
/*
|
||||
* Every other request must be forwarded to the appropriate fsvol device. If there is no
|
||||
* fsvol device, then we must return the appropriate status code (see below).
|
||||
*
|
||||
* Please note that since we allow the fsmup device to be opened, we must also handle
|
||||
* CLEANUP and CLOSE requests for it.
|
||||
*/
|
||||
|
||||
if (0 != FileObject)
|
||||
{
|
||||
if (FspFileNodeIsValid(FileObject->FsContext))
|
||||
FsvolDeviceObject = ((FSP_FILE_NODE *)FileObject->FsContext)->FsvolDeviceObject;
|
||||
else if (0 != FileObject->FsContext2 &&
|
||||
3 == ((PDEVICE_OBJECT)FileObject->FsContext2)->Type &&
|
||||
0 != ((PDEVICE_OBJECT)FileObject->FsContext2)->DeviceExtension &&
|
||||
FspFsvolDeviceExtensionKind == FspDeviceExtension((PDEVICE_OBJECT)FileObject->FsContext2)->Kind)
|
||||
FsvolDeviceObject = (PDEVICE_OBJECT)FileObject->FsContext2;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (0 == FsvolDeviceObject)
|
||||
{
|
||||
/*
|
||||
* We were not able to find an fsvol device to forward this IRP to. We will complete
|
||||
* the IRP with an appropriate status code.
|
||||
*/
|
||||
|
||||
switch (IrpSp->MajorFunction)
|
||||
{
|
||||
case IRP_MJ_CLEANUP:
|
||||
case IRP_MJ_CLOSE:
|
||||
/*
|
||||
* CLEANUP and CLOSE requests ignore their status code (except for STATUS_PENDING).
|
||||
* So return STATUS_SUCCESS. This works regardless of whether this is a legitimate
|
||||
* fsmup request or an erroneous CLOSE request that we should not have seen.
|
||||
*/
|
||||
Irp->IoStatus.Status = STATUS_SUCCESS;
|
||||
break;
|
||||
case IRP_MJ_QUERY_INFORMATION:
|
||||
case IRP_MJ_SET_INFORMATION:
|
||||
Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
|
||||
break;
|
||||
default:
|
||||
Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST;
|
||||
break;
|
||||
}
|
||||
|
||||
Irp->IoStatus.Information = 0;
|
||||
IoCompleteRequest(Irp, FSP_IO_INCREMENT);
|
||||
Result = Irp->IoStatus.Status;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
ASSERT(FspFsvolDeviceExtensionKind == FspDeviceExtension(FsvolDeviceObject)->Kind);
|
||||
|
||||
/*
|
||||
* Forward the IRP to the appropriate fsvol device. The fsvol device will take care
|
||||
* to complete the IRP, etc.
|
||||
*/
|
||||
IoSkipCurrentIrpStackLocation(Irp);
|
||||
Result = IoCallDriver(FsvolDeviceObject, Irp);
|
||||
|
||||
if (DeviceDeref)
|
||||
FspDeviceDereference(FsvolDeviceObject);
|
||||
|
||||
exit:
|
||||
FsRtlExitFileSystem();
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
static NTSTATUS FspMupRedirQueryPathEx(
|
||||
PDEVICE_OBJECT FsmupDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp)
|
||||
{
|
||||
PAGED_CODE();
|
||||
|
||||
ASSERT(IRP_MJ_DEVICE_CONTROL == IrpSp->MajorFunction);
|
||||
ASSERT(IOCTL_REDIR_QUERY_PATH_EX == IrpSp->Parameters.DeviceIoControl.IoControlCode);
|
||||
|
||||
Irp->IoStatus.Information = 0;
|
||||
|
||||
if (KernelMode != Irp->RequestorMode)
|
||||
return STATUS_INVALID_DEVICE_REQUEST;
|
||||
|
||||
/* check parameters */
|
||||
ULONG InputBufferLength = IrpSp->Parameters.DeviceIoControl.InputBufferLength;
|
||||
ULONG OutputBufferLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength;
|
||||
QUERY_PATH_REQUEST_EX *QueryPathRequest = IrpSp->Parameters.DeviceIoControl.Type3InputBuffer;
|
||||
QUERY_PATH_RESPONSE *QueryPathResponse = Irp->UserBuffer;
|
||||
if (sizeof(QUERY_PATH_REQUEST_EX) > InputBufferLength ||
|
||||
0 == QueryPathRequest || 0 == QueryPathResponse)
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
if (sizeof(QUERY_PATH_RESPONSE) > OutputBufferLength)
|
||||
return STATUS_BUFFER_TOO_SMALL;
|
||||
|
||||
NTSTATUS Result;
|
||||
FSP_FSMUP_DEVICE_EXTENSION *FsmupDeviceExtension = FspFsmupDeviceExtension(FsmupDeviceObject);
|
||||
FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension;
|
||||
PUNICODE_PREFIX_TABLE_ENTRY Entry;
|
||||
PDEVICE_OBJECT FsvolDeviceObject = 0;
|
||||
|
||||
Result = STATUS_BAD_NETWORK_PATH;
|
||||
ExAcquireResourceExclusiveLite(&FsmupDeviceExtension->PrefixTableResource, TRUE);
|
||||
Entry = RtlFindUnicodePrefix(&FsmupDeviceExtension->PrefixTable,
|
||||
&QueryPathRequest->PathName, 0);
|
||||
if (0 != Entry)
|
||||
{
|
||||
FsvolDeviceExtension = CONTAINING_RECORD(Entry, FSP_FSVOL_DEVICE_EXTENSION, VolumePrefixEntry);
|
||||
FsvolDeviceObject = FsvolDeviceExtension->FsvolDeviceObject;
|
||||
if (!FspIoqStopped(FsvolDeviceExtension->Ioq))
|
||||
{
|
||||
if (0 < FsvolDeviceExtension->VolumePrefix.Length &&
|
||||
FspFsvolDeviceVolumePrefixInString(FsvolDeviceObject, &QueryPathRequest->PathName) &&
|
||||
(QueryPathRequest->PathName.Length == FsvolDeviceExtension->VolumePrefix.Length ||
|
||||
'\\' == QueryPathRequest->PathName.Buffer[FsvolDeviceExtension->VolumePrefix.Length / sizeof(WCHAR)]))
|
||||
{
|
||||
QueryPathResponse->LengthAccepted = FsvolDeviceExtension->VolumePrefix.Length;
|
||||
Result = STATUS_SUCCESS;
|
||||
}
|
||||
}
|
||||
}
|
||||
ExReleaseResourceLite(&FsmupDeviceExtension->PrefixTableResource);
|
||||
|
||||
return Result;
|
||||
}
|
Reference in New Issue
Block a user