mirror of
https://github.com/winfsp/winfsp.git
synced 2025-04-22 08:23:05 -05:00
332 lines
11 KiB
C
332 lines
11 KiB
C
/**
|
|
* @file sys/iop.c
|
|
*
|
|
* @copyright 2015-2016 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>
|
|
|
|
NTSTATUS FspIopCreateRequestFunnel(
|
|
PIRP Irp, PUNICODE_STRING FileName, ULONG ExtraSize, FSP_IOP_REQUEST_FINI *RequestFini,
|
|
ULONG Flags, FSP_FSCTL_TRANSACT_REQ **PRequest);
|
|
VOID FspIopDeleteRequest(FSP_FSCTL_TRANSACT_REQ *Request);
|
|
VOID FspIopResetRequest(FSP_FSCTL_TRANSACT_REQ *Request, FSP_IOP_REQUEST_FINI *RequestFini);
|
|
NTSTATUS FspIopPostWorkRequestFunnel(PDEVICE_OBJECT DeviceObject,
|
|
FSP_FSCTL_TRANSACT_REQ *Request, BOOLEAN AllocateIrpMustSucceed);
|
|
static IO_COMPLETION_ROUTINE FspIopPostWorkRequestCompletion;
|
|
VOID FspIopCompleteIrpEx(PIRP Irp, NTSTATUS Result, BOOLEAN DeviceDereference);
|
|
VOID FspIopCompleteCanceledIrp(PIRP Irp);
|
|
BOOLEAN FspIopRetryPrepareIrp(PIRP Irp, NTSTATUS *PResult);
|
|
BOOLEAN FspIopRetryCompleteIrp(PIRP Irp, const FSP_FSCTL_TRANSACT_RSP *Response, NTSTATUS *PResult);
|
|
FSP_FSCTL_TRANSACT_RSP *FspIopIrpResponse(PIRP Irp);
|
|
NTSTATUS FspIopDispatchPrepare(PIRP Irp, FSP_FSCTL_TRANSACT_REQ *Request);
|
|
NTSTATUS FspIopDispatchComplete(PIRP Irp, const FSP_FSCTL_TRANSACT_RSP *Response);
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE, FspIopCreateRequestFunnel)
|
|
#pragma alloc_text(PAGE, FspIopDeleteRequest)
|
|
#pragma alloc_text(PAGE, FspIopResetRequest)
|
|
#pragma alloc_text(PAGE, FspIopPostWorkRequestFunnel)
|
|
#pragma alloc_text(PAGE, FspIopCompleteIrpEx)
|
|
#pragma alloc_text(PAGE, FspIopCompleteCanceledIrp)
|
|
#pragma alloc_text(PAGE, FspIopRetryPrepareIrp)
|
|
#pragma alloc_text(PAGE, FspIopRetryCompleteIrp)
|
|
#pragma alloc_text(PAGE, FspIopIrpResponse)
|
|
#pragma alloc_text(PAGE, FspIopDispatchPrepare)
|
|
#pragma alloc_text(PAGE, FspIopDispatchComplete)
|
|
#endif
|
|
|
|
/* Requests (and RequestHeaders) must be 16-byte aligned, because we use the low 4 bits for flags */
|
|
#if REQ_ALIGN_SIZE <= MEMORY_ALLOCATION_ALIGNMENT
|
|
#define REQ_HEADER_ALIGN_MASK 0
|
|
#define REQ_HEADER_ALIGN_OVERHEAD 0
|
|
#else
|
|
#define REQ_HEADER_ALIGN_MASK (REQ_ALIGN_SIZE - 1)
|
|
#define REQ_HEADER_ALIGN_OVERHEAD (sizeof(PVOID) + REQ_HEADER_ALIGN_MASK)
|
|
#endif
|
|
|
|
NTSTATUS FspIopCreateRequestFunnel(
|
|
PIRP Irp, PUNICODE_STRING FileName, ULONG ExtraSize, FSP_IOP_REQUEST_FINI *RequestFini,
|
|
ULONG Flags, FSP_FSCTL_TRANSACT_REQ **PRequest)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
FSP_FSCTL_TRANSACT_REQ_HEADER *RequestHeader;
|
|
FSP_FSCTL_TRANSACT_REQ *Request;
|
|
|
|
*PRequest = 0;
|
|
|
|
if (0 != FileName)
|
|
ExtraSize += FSP_FSCTL_DEFAULT_ALIGN_UP(FileName->Length + sizeof(WCHAR));
|
|
|
|
if (FSP_FSCTL_TRANSACT_REQ_SIZEMAX < sizeof *Request + ExtraSize)
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
if (FlagOn(Flags, FspIopRequestMustSucceed))
|
|
RequestHeader = FspAllocatePoolMustSucceed(
|
|
FlagOn(Flags, FspIopRequestNonPaged) ? NonPagedPool : PagedPool,
|
|
sizeof *RequestHeader + sizeof *Request + ExtraSize + REQ_HEADER_ALIGN_OVERHEAD,
|
|
FSP_ALLOC_INTERNAL_TAG);
|
|
else
|
|
{
|
|
RequestHeader = ExAllocatePoolWithTag(
|
|
FlagOn(Flags, FspIopRequestNonPaged) ? NonPagedPool : PagedPool,
|
|
sizeof *RequestHeader + sizeof *Request + ExtraSize + REQ_HEADER_ALIGN_OVERHEAD,
|
|
FSP_ALLOC_INTERNAL_TAG);
|
|
if (0 == RequestHeader)
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
#if 0 != REQ_HEADER_ALIGN_MASK
|
|
PVOID Allocation = RequestHeader;
|
|
RequestHeader = (PVOID)(((UINT_PTR)RequestHeader + REQ_HEADER_ALIGN_OVERHEAD) &
|
|
~REQ_HEADER_ALIGN_MASK);
|
|
((PVOID *)RequestHeader)[-1] = Allocation;
|
|
#endif
|
|
|
|
RtlZeroMemory(RequestHeader, sizeof *RequestHeader + sizeof *Request + ExtraSize);
|
|
RequestHeader->RequestFini = RequestFini;
|
|
|
|
Request = (PVOID)RequestHeader->RequestBuf;
|
|
Request->Size = (UINT16)(sizeof *Request + ExtraSize);
|
|
Request->Hint = (UINT_PTR)Irp;
|
|
if (0 != FileName)
|
|
{
|
|
RtlCopyMemory(Request->Buffer, FileName->Buffer, FileName->Length);
|
|
//Request->Buffer[FileName->Length] = '\0';
|
|
//Request->Buffer[FileName->Length + 1] = '\0';
|
|
//Request->FileName.Offset = 0;
|
|
Request->FileName.Size = FileName->Length + sizeof(WCHAR);
|
|
}
|
|
|
|
if (0 != Irp)
|
|
{
|
|
ASSERT(0 == FspIrpRequest(Irp));
|
|
FspIrpSetRequest(Irp, Request);
|
|
}
|
|
*PRequest = Request;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
VOID FspIopDeleteRequest(FSP_FSCTL_TRANSACT_REQ *Request)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
FSP_FSCTL_TRANSACT_REQ_HEADER *RequestHeader = (PVOID)((PUINT8)Request - sizeof *RequestHeader);
|
|
|
|
if (0 != RequestHeader->RequestFini)
|
|
RequestHeader->RequestFini(Request, RequestHeader->Context);
|
|
|
|
if (0 != RequestHeader->Response)
|
|
FspFree(RequestHeader->Response);
|
|
|
|
#if 0 != REQ_HEADER_ALIGN_MASK
|
|
RequestHeader = ((PVOID *)RequestHeader)[-1];
|
|
#endif
|
|
|
|
FspFree(RequestHeader);
|
|
}
|
|
|
|
VOID FspIopResetRequest(FSP_FSCTL_TRANSACT_REQ *Request, FSP_IOP_REQUEST_FINI *RequestFini)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
FSP_FSCTL_TRANSACT_REQ_HEADER *RequestHeader = (PVOID)((PUINT8)Request - sizeof *RequestHeader);
|
|
|
|
if (0 != RequestHeader->RequestFini)
|
|
RequestHeader->RequestFini(Request, RequestHeader->Context);
|
|
|
|
RtlZeroMemory(&RequestHeader->Context, sizeof RequestHeader->Context);
|
|
RequestHeader->RequestFini = RequestFini;
|
|
}
|
|
|
|
NTSTATUS FspIopPostWorkRequestFunnel(PDEVICE_OBJECT DeviceObject,
|
|
FSP_FSCTL_TRANSACT_REQ *Request, BOOLEAN BestEffort)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
ASSERT(0 == Request->Hint);
|
|
|
|
NTSTATUS Result;
|
|
PIRP Irp;
|
|
|
|
if (BestEffort)
|
|
Irp = FspAllocateIrpMustSucceed(DeviceObject->StackSize);
|
|
else
|
|
{
|
|
Irp = IoAllocateIrp(DeviceObject->StackSize, FALSE);
|
|
if (0 == Irp)
|
|
{
|
|
Result = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
PIO_STACK_LOCATION IrpSp = IoGetNextIrpStackLocation(Irp);
|
|
Irp->RequestorMode = KernelMode;
|
|
IrpSp->MajorFunction = IRP_MJ_FILE_SYSTEM_CONTROL;
|
|
IrpSp->MinorFunction = IRP_MN_USER_FS_REQUEST;
|
|
IrpSp->Parameters.FileSystemControl.FsControlCode = BestEffort ?
|
|
FSP_FSCTL_WORK_BEST_EFFORT : FSP_FSCTL_WORK;
|
|
IrpSp->Parameters.FileSystemControl.InputBufferLength = Request->Size;
|
|
IrpSp->Parameters.FileSystemControl.Type3InputBuffer = Request;
|
|
|
|
ASSERT(METHOD_NEITHER == (IrpSp->Parameters.DeviceIoControl.IoControlCode & 3));
|
|
|
|
IoSetCompletionRoutine(Irp, FspIopPostWorkRequestCompletion, 0, TRUE, TRUE, TRUE);
|
|
|
|
Result = IoCallDriver(DeviceObject, Irp);
|
|
if (STATUS_PENDING == Result)
|
|
return STATUS_SUCCESS;
|
|
|
|
/*
|
|
* If we did not receive STATUS_PENDING, we still own the Request and must delete it!
|
|
*/
|
|
|
|
exit:
|
|
FspIopDeleteRequest(Request);
|
|
|
|
return Result;
|
|
}
|
|
|
|
static NTSTATUS FspIopPostWorkRequestCompletion(
|
|
PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID Context)
|
|
{
|
|
// !PAGED_CODE();
|
|
|
|
IoFreeIrp(Irp);
|
|
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
}
|
|
|
|
VOID FspIopCompleteIrpEx(PIRP Irp, NTSTATUS Result, BOOLEAN DeviceDereference)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
ASSERT(STATUS_PENDING != Result);
|
|
ASSERT(0 == (FSP_STATUS_PRIVATE_BIT & Result));
|
|
|
|
if (0 != FspIrpRequest(Irp))
|
|
{
|
|
FspIopDeleteRequest(FspIrpRequest(Irp));
|
|
FspIrpSetRequest(Irp, 0);
|
|
}
|
|
|
|
/* get the device object out of the IRP before completion */
|
|
PDEVICE_OBJECT DeviceObject = IoGetCurrentIrpStackLocation(Irp)->DeviceObject;
|
|
|
|
if (STATUS_SUCCESS != Result && STATUS_REPARSE != Result && STATUS_BUFFER_OVERFLOW != Result)
|
|
Irp->IoStatus.Information = 0;
|
|
Irp->IoStatus.Status = Result;
|
|
IoCompleteRequest(Irp, FSP_IO_INCREMENT);
|
|
|
|
if (DeviceDereference)
|
|
FspDeviceDereference(DeviceObject);
|
|
}
|
|
|
|
VOID FspIopCompleteCanceledIrp(PIRP Irp)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
DEBUGLOGIRP(Irp, STATUS_CANCELLED);
|
|
|
|
/*
|
|
* An IRP cancel may happen at any time including when APC's are still enabled.
|
|
* For this reason we execute FsRtlEnterFileSystem/FsRtlExitFileSystem here.
|
|
* This will protect ERESOURCE operations during Request finalizations.
|
|
*/
|
|
FsRtlEnterFileSystem();
|
|
|
|
PIRP TopLevelIrp = IoGetTopLevelIrp();
|
|
IoSetTopLevelIrp(Irp);
|
|
|
|
FspIopCompleteIrpEx(Irp, STATUS_CANCELLED, TRUE);
|
|
|
|
IoSetTopLevelIrp(TopLevelIrp);
|
|
|
|
FsRtlExitFileSystem();
|
|
}
|
|
|
|
BOOLEAN FspIopRetryPrepareIrp(PIRP Irp, NTSTATUS *PResult)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
PDEVICE_OBJECT DeviceObject = IoGetCurrentIrpStackLocation(Irp)->DeviceObject;
|
|
FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(DeviceObject);
|
|
|
|
return FspIoqPostIrpBestEffort(FsvolDeviceExtension->Ioq, Irp, PResult);
|
|
}
|
|
|
|
BOOLEAN FspIopRetryCompleteIrp(PIRP Irp, const FSP_FSCTL_TRANSACT_RSP *Response, NTSTATUS *PResult)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
PDEVICE_OBJECT DeviceObject = IoGetCurrentIrpStackLocation(Irp)->DeviceObject;
|
|
FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(DeviceObject);
|
|
FSP_FSCTL_TRANSACT_REQ *Request = FspIrpRequest(Irp);
|
|
FSP_FSCTL_TRANSACT_REQ_HEADER *RequestHeader = (PVOID)((PUINT8)Request - sizeof *RequestHeader);
|
|
|
|
ASSERT(0 != Request);
|
|
|
|
if (0 != Response && RequestHeader->Response != Response)
|
|
{
|
|
if (0 != RequestHeader->Response)
|
|
FspFree(RequestHeader->Response);
|
|
RequestHeader->Response = FspAllocMustSucceed(Response->Size);
|
|
RtlCopyMemory(RequestHeader->Response, Response, Response->Size);
|
|
Response = RequestHeader->Response;
|
|
}
|
|
|
|
return FspIoqRetryCompleteIrp(FsvolDeviceExtension->Ioq, Irp, PResult);
|
|
}
|
|
|
|
FSP_FSCTL_TRANSACT_RSP *FspIopIrpResponse(PIRP Irp)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
FSP_FSCTL_TRANSACT_REQ *Request = FspIrpRequest(Irp);
|
|
FSP_FSCTL_TRANSACT_REQ_HEADER *RequestHeader = (PVOID)((PUINT8)Request - sizeof *RequestHeader);
|
|
|
|
return RequestHeader->Response;
|
|
}
|
|
|
|
NTSTATUS FspIopDispatchPrepare(PIRP Irp, FSP_FSCTL_TRANSACT_REQ *Request)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
ASSERT(IRP_MJ_MAXIMUM_FUNCTION >= IrpSp->MajorFunction);
|
|
if (0 != FspIopPrepareFunction[IrpSp->MajorFunction])
|
|
return FspIopPrepareFunction[IrpSp->MajorFunction](Irp, Request);
|
|
else
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS FspIopDispatchComplete(PIRP Irp, const FSP_FSCTL_TRANSACT_RSP *Response)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
ASSERT(IRP_MJ_MAXIMUM_FUNCTION >= IrpSp->MajorFunction);
|
|
ASSERT(0 != FspIopCompleteFunction[IrpSp->MajorFunction]);
|
|
|
|
return FspIopCompleteFunction[IrpSp->MajorFunction](Irp, Response);
|
|
}
|
|
|
|
FSP_IOPREP_DISPATCH *FspIopPrepareFunction[IRP_MJ_MAXIMUM_FUNCTION + 1];
|
|
FSP_IOCMPL_DISPATCH *FspIopCompleteFunction[IRP_MJ_MAXIMUM_FUNCTION + 1];
|