From 4b43cc590f409da3c76a6077e59f1410963afe42 Mon Sep 17 00:00:00 2001 From: Bill Zissimopoulos Date: Thu, 23 Feb 2017 16:06:55 -0800 Subject: [PATCH] sys: initial ProcessBuffer implementation --- build/VStudio/winfsp_sys.vcxproj | 1 + build/VStudio/winfsp_sys.vcxproj.filters | 3 + inc/winfsp/fsctl.h | 3 +- src/sys/dirctl.c | 162 +++++++++---- src/sys/driver.c | 15 +- src/sys/driver.h | 25 ++ src/sys/psbuffer.c | 280 +++++++++++++++++++++++ src/sys/read.c | 177 ++++++++++---- src/sys/write.c | 163 +++++++++---- tst/memfs/memfs.cpp | 4 +- 10 files changed, 694 insertions(+), 139 deletions(-) create mode 100644 src/sys/psbuffer.c diff --git a/build/VStudio/winfsp_sys.vcxproj b/build/VStudio/winfsp_sys.vcxproj index 2e9e9a38..6fcfd949 100644 --- a/build/VStudio/winfsp_sys.vcxproj +++ b/build/VStudio/winfsp_sys.vcxproj @@ -174,6 +174,7 @@ + diff --git a/build/VStudio/winfsp_sys.vcxproj.filters b/build/VStudio/winfsp_sys.vcxproj.filters index bef315d5..eb0c4f04 100644 --- a/build/VStudio/winfsp_sys.vcxproj.filters +++ b/build/VStudio/winfsp_sys.vcxproj.filters @@ -98,6 +98,9 @@ Source + + Source + diff --git a/inc/winfsp/fsctl.h b/inc/winfsp/fsctl.h index 5ec57d86..7c252f1b 100644 --- a/inc/winfsp/fsctl.h +++ b/inc/winfsp/fsctl.h @@ -148,7 +148,8 @@ typedef struct /* kernel-mode flags */ UINT32 PostCleanupWhenModifiedOnly:1; /* post Cleanup when a file was modified/deleted */ UINT32 PassQueryDirectoryPattern:1; /* pass Pattern during QueryDirectory operations */ - UINT32 KmReservedFlags:4; + UINT32 AlwaysUseDoubleBuffering:1; + UINT32 KmReservedFlags:3; /* user-mode flags */ UINT32 UmFileContextIsUserContext2:1; /* user mode: FileContext parameter is UserContext2 */ UINT32 UmFileContextIsFullContext:1; /* user mode: FileContext parameter is FullContext */ diff --git a/src/sys/dirctl.c b/src/sys/dirctl.c index dddf4196..a6c115c5 100644 --- a/src/sys/dirctl.c +++ b/src/sys/dirctl.c @@ -78,6 +78,7 @@ enum { /* QueryDirectory */ RequestIrp = 0, + RequestCookie = 1, RequestMdl = 1, RequestAddress = 2, RequestProcess = 3, @@ -833,41 +834,68 @@ NTSTATUS FspFsvolDirectoryControlPrepare( { PAGED_CODE(); - NTSTATUS Result; - PMDL Mdl = 0; - PVOID Address; - PEPROCESS Process; - - Mdl = IoAllocateMdl( - Irp->AssociatedIrp.SystemBuffer, - Request->Req.QueryDirectory.Length, - FALSE, FALSE, 0); - if (0 == Mdl) - return STATUS_INSUFFICIENT_RESOURCES; - - MmBuildMdlForNonPagedPool(Mdl); - - /* map the MDL into user-mode */ - Result = FspMapLockedPagesInUserMode(Mdl, &Address, 0); - if (!NT_SUCCESS(Result)) + if (FspFsvolDeviceQueryDirectoryShouldUseProcessBuffer( + IoGetCurrentIrpStackLocation(Irp)->DeviceObject, Request->Req.QueryDirectory.Length)) { - if (0 != Mdl) - IoFreeMdl(Mdl); + NTSTATUS Result; + PVOID Cookie; + PVOID Address; + PEPROCESS Process; - return Result; + Result = FspProcessBufferAcquire(Request->Req.QueryDirectory.Length, &Cookie, &Address); + if (!NT_SUCCESS(Result)) + return Result; + + /* get a pointer to the current process so that we can release the buffer later */ + Process = PsGetCurrentProcess(); + ObReferenceObject(Process); + + Request->Req.QueryDirectory.Address = (UINT64)(UINT_PTR)Address; + + FspIopRequestContext(Request, RequestCookie) = Cookie; + FspIopRequestContext(Request, RequestAddress) = Address; + FspIopRequestContext(Request, RequestProcess) = Process; + + return STATUS_SUCCESS; } + else + { + NTSTATUS Result; + PMDL Mdl = 0; + PVOID Address; + PEPROCESS Process; - /* get a pointer to the current process so that we can unmap the address later */ - Process = PsGetCurrentProcess(); - ObReferenceObject(Process); + Mdl = IoAllocateMdl( + Irp->AssociatedIrp.SystemBuffer, + Request->Req.QueryDirectory.Length, + FALSE, FALSE, 0); + if (0 == Mdl) + return STATUS_INSUFFICIENT_RESOURCES; - Request->Req.QueryDirectory.Address = (UINT64)(UINT_PTR)Address; + MmBuildMdlForNonPagedPool(Mdl); - FspIopRequestContext(Request, RequestMdl) = Mdl; - FspIopRequestContext(Request, RequestAddress) = Address; - FspIopRequestContext(Request, RequestProcess) = Process; + /* map the MDL into user-mode */ + Result = FspMapLockedPagesInUserMode(Mdl, &Address, 0); + if (!NT_SUCCESS(Result)) + { + if (0 != Mdl) + IoFreeMdl(Mdl); - return STATUS_SUCCESS; + return Result; + } + + /* get a pointer to the current process so that we can unmap the address later */ + Process = PsGetCurrentProcess(); + ObReferenceObject(Process); + + Request->Req.QueryDirectory.Address = (UINT64)(UINT_PTR)Address; + + FspIopRequestContext(Request, RequestMdl) = Mdl; + FspIopRequestContext(Request, RequestAddress) = Address; + FspIopRequestContext(Request, RequestProcess) = Process; + + return STATUS_SUCCESS; + } } NTSTATUS FspFsvolDirectoryControlComplete( @@ -910,6 +938,24 @@ NTSTATUS FspFsvolDirectoryControlComplete( if (FspFsctlTransactQueryDirectoryKind == Request->Kind) { + if (FspFsvolDeviceQueryDirectoryShouldUseProcessBuffer( + IrpSp->DeviceObject, Request->Req.QueryDirectory.Length)) + { + PVOID Address = FspIopRequestContext(Request, RequestAddress); + + ASSERT(0 != Address); + try + { + RtlCopyMemory(Irp->AssociatedIrp.SystemBuffer, Address, Response->IoStatus.Information); + } + except (EXCEPTION_EXECUTE_HANDLER) + { + Result = GetExceptionCode(); + Result = FsRtlIsNtstatusExpected(Result) ? STATUS_INVALID_USER_BUFFER : Result; + FSP_RETURN(); + } + } + DirInfoChangeNumber = FspFileNodeDirInfoChangeNumber(FileNode); Request->Kind = FspFsctlTransactReservedKind; FspIopResetRequest(Request, 0); @@ -1010,29 +1056,57 @@ static VOID FspFsvolQueryDirectoryRequestFini(FSP_FSCTL_TRANSACT_REQ *Request, P PAGED_CODE(); PIRP Irp = Context[RequestIrp]; - PMDL Mdl = Context[RequestMdl]; - PVOID Address = Context[RequestAddress]; - PEPROCESS Process = Context[RequestProcess]; - if (0 != Address) + if (0 != Irp && FspFsvolDeviceQueryDirectoryShouldUseProcessBuffer( + IoGetCurrentIrpStackLocation(Irp)->DeviceObject, Request->Req.QueryDirectory.Length)) { - KAPC_STATE ApcState; - BOOLEAN Attach; + PVOID Cookie = Context[RequestCookie]; + PVOID Address = Context[RequestAddress]; + PEPROCESS Process = Context[RequestProcess]; - ASSERT(0 != Process); - Attach = Process != PsGetCurrentProcess(); + if (0 != Address) + { + KAPC_STATE ApcState; + BOOLEAN Attach; - if (Attach) - KeStackAttachProcess(Process, &ApcState); - MmUnmapLockedPages(Address, Mdl); - if (Attach) - KeUnstackDetachProcess(&ApcState); + ASSERT(0 != Process); + Attach = Process != PsGetCurrentProcess(); - ObDereferenceObject(Process); + if (Attach) + KeStackAttachProcess(Process, &ApcState); + FspProcessBufferRelease(Cookie, Address); + if (Attach) + KeUnstackDetachProcess(&ApcState); + + ObDereferenceObject(Process); + } } + else + { + PMDL Mdl = Context[RequestMdl]; + PVOID Address = Context[RequestAddress]; + PEPROCESS Process = Context[RequestProcess]; - if (0 != Mdl) - IoFreeMdl(Mdl); + if (0 != Address) + { + KAPC_STATE ApcState; + BOOLEAN Attach; + + ASSERT(0 != Process); + Attach = Process != PsGetCurrentProcess(); + + if (Attach) + KeStackAttachProcess(Process, &ApcState); + MmUnmapLockedPages(Address, Mdl); + if (Attach) + KeUnstackDetachProcess(&ApcState); + + ObDereferenceObject(Process); + } + + if (0 != Mdl) + IoFreeMdl(Mdl); + } if (0 != Irp) { diff --git a/src/sys/driver.c b/src/sys/driver.c index 5d0a9e7b..1cbf9e46 100644 --- a/src/sys/driver.c +++ b/src/sys/driver.c @@ -46,6 +46,10 @@ NTSTATUS DriverEntry( FspDriverMultiVersionInitialize(); + Result = FspProcessBufferInitialize(); + if (!NT_SUCCESS(Result)) + FSP_RETURN(); + FspDriverObject = DriverObject; ExInitializeResourceLite(&FspDeviceGlobalResource); @@ -59,14 +63,21 @@ NTSTATUS DriverEntry( &DeviceSddl, &FspFsctlDeviceClassGuid, &FspFsctlDiskDeviceObject); if (!NT_SUCCESS(Result)) + { + FspProcessBufferFinalize(); FSP_RETURN(); + } RtlInitUnicodeString(&DeviceName, L"\\Device\\" FSP_FSCTL_NET_DEVICE_NAME); Result = FspDeviceCreateSecure(FspFsctlDeviceExtensionKind, 0, &DeviceName, FILE_DEVICE_NETWORK_FILE_SYSTEM, FILE_DEVICE_SECURE_OPEN, &DeviceSddl, &FspFsctlDeviceClassGuid, &FspFsctlNetDeviceObject); if (!NT_SUCCESS(Result)) - FSP_RETURN(FspDeviceDelete(FspFsctlDiskDeviceObject)); + { + FspDeviceDelete(FspFsctlDiskDeviceObject); + FspProcessBufferFinalize(); + FSP_RETURN(); + } Result = FspDeviceInitialize(FspFsctlDiskDeviceObject); ASSERT(STATUS_SUCCESS == Result); Result = FspDeviceInitialize(FspFsctlNetDeviceObject); @@ -207,6 +218,8 @@ VOID FspUnload( ExDeleteResourceLite(&FspDeviceGlobalResource); FspDriverObject = 0; + FspProcessBufferFinalize(); + #pragma prefast(suppress:28175, "We are in DriverUnload: ok to access DriverName") FSP_LEAVE_VOID("DriverName=\"%wZ\"", &DriverObject->DriverName); diff --git a/src/sys/driver.h b/src/sys/driver.h index b7847b8d..2c7ff6ad 100644 --- a/src/sys/driver.h +++ b/src/sys/driver.h @@ -601,6 +601,14 @@ VOID FspIrpHookReset(PIRP Irp); PVOID FspIrpHookContext(PVOID Context); NTSTATUS FspIrpHookNext(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID Context); +/* process buffers */ +#define FspProcessBufferSizeMax (64 * 1024) +NTSTATUS FspProcessBufferInitialize(VOID); +VOID FspProcessBufferFinalize(VOID); +VOID FspProcessBufferCollect(HANDLE ProcessId); +NTSTATUS FspProcessBufferAcquire(SIZE_T BufferSize, PVOID *PBufferCookie, PVOID *PBuffer); +VOID FspProcessBufferRelease(PVOID BufferCookie, PVOID Buffer); + /* IRP context */ #define FspIrpTimestampInfinity ((ULONG)-1L) #define FspIrpTimestamp(Irp) \ @@ -1048,6 +1056,23 @@ FSP_FSVOL_DEVICE_EXTENSION *FspFsvolDeviceExtension(PDEVICE_OBJECT DeviceObject) ASSERT(FspFsvolDeviceExtensionKind == ((FSP_DEVICE_EXTENSION *)DeviceObject->DeviceExtension)->Kind); return DeviceObject->DeviceExtension; } +static inline +BOOLEAN FspFsvolDeviceReadShouldUseProcessBuffer(PDEVICE_OBJECT FsvolDeviceObject, SIZE_T BufferSize) +{ + return FspProcessBufferSizeMax >= BufferSize || + FspFsvolDeviceExtension(FsvolDeviceObject)->VolumeParams.AlwaysUseDoubleBuffering; +} +static inline +BOOLEAN FspFsvolDeviceWriteShouldUseProcessBuffer(PDEVICE_OBJECT FsvolDeviceObject, SIZE_T BufferSize) +{ + return FspProcessBufferSizeMax >= BufferSize || + FspFsvolDeviceExtension(FsvolDeviceObject)->VolumeParams.AlwaysUseDoubleBuffering; +} +static inline +BOOLEAN FspFsvolDeviceQueryDirectoryShouldUseProcessBuffer(PDEVICE_OBJECT FsvolDeviceObject, SIZE_T BufferSize) +{ + return FspFsvolDeviceReadShouldUseProcessBuffer(FsvolDeviceObject, BufferSize); +} NTSTATUS FspDeviceCreateSecure(UINT32 Kind, ULONG ExtraSize, PUNICODE_STRING DeviceName, DEVICE_TYPE DeviceType, ULONG DeviceCharacteristics, PUNICODE_STRING DeviceSddl, LPCGUID DeviceClassGuid, diff --git a/src/sys/psbuffer.c b/src/sys/psbuffer.c new file mode 100644 index 00000000..6d44022b --- /dev/null +++ b/src/sys/psbuffer.c @@ -0,0 +1,280 @@ +/** + * @file sys/psbuffer.c + * + * @copyright 2015-2017 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 + +#define SafeGetCurrentProcessId() (PsGetProcessId(PsGetCurrentProcess())) + +#define FspProcessBufferCountMax (2 >= FspProcessorCount ? 2 : (8 <= FspProcessorCount ? 8 : FspProcessorCount)) +#define ProcessBufferBucketCount 61 /* are you going to have that many file systems? */ + +typedef struct _FSP_PROCESS_BUFFER_ITEM +{ + struct _FSP_PROCESS_BUFFER_ITEM *DictNext; + struct _FSP_PROCESS_BUFFER_LIST_ENTRY *BufferList; + ULONG BufferCount; + HANDLE ProcessId; +} FSP_PROCESS_BUFFER_ITEM; + +typedef struct _FSP_PROCESS_BUFFER_LIST_ENTRY +{ + struct _FSP_PROCESS_BUFFER_LIST_ENTRY *Next; + PVOID Buffer; +} FSP_PROCESS_BUFFER_LIST_ENTRY; + +static KSPIN_LOCK ProcessBufferLock; +static FSP_PROCESS_BUFFER_ITEM *ProcessBufferBuckets[ProcessBufferBucketCount]; + +static VOID FspProcessBufferNotifyRoutine(HANDLE ParentId, HANDLE ProcessId, BOOLEAN Create); + +static inline FSP_PROCESS_BUFFER_ITEM *FspProcessBufferLookupItemAtDpcLevel(HANDLE ProcessId) +{ + FSP_PROCESS_BUFFER_ITEM *Item = 0; + ULONG HashIndex = FspHashMixPointer(ProcessId) % ProcessBufferBucketCount; + for (FSP_PROCESS_BUFFER_ITEM *ItemX = ProcessBufferBuckets[HashIndex]; ItemX; ItemX = ItemX->DictNext) + if (ItemX->ProcessId == ProcessId) + { + Item = ItemX; + break; + } + return Item; +} + +static inline VOID FspProcessBufferAddItemAtDpcLevel(FSP_PROCESS_BUFFER_ITEM *Item) +{ + ULONG HashIndex = FspHashMixPointer(Item->ProcessId) % ProcessBufferBucketCount; +#if DBG + for (FSP_PROCESS_BUFFER_ITEM *ItemX = ProcessBufferBuckets[HashIndex]; ItemX; ItemX = ItemX->DictNext) + ASSERT(ItemX->ProcessId != Item->ProcessId); +#endif + Item->DictNext = ProcessBufferBuckets[HashIndex]; + ProcessBufferBuckets[HashIndex] = Item; +} + +static inline FSP_PROCESS_BUFFER_ITEM *FspProcessBufferRemoveItemAtDpcLevel(HANDLE ProcessId) +{ + FSP_PROCESS_BUFFER_ITEM *Item = 0; + ULONG HashIndex = FspHashMixPointer(ProcessId) % ProcessBufferBucketCount; + for (FSP_PROCESS_BUFFER_ITEM **P = &ProcessBufferBuckets[HashIndex]; *P; P = &(*P)->DictNext) + if ((*P)->ProcessId == ProcessId) + { + Item = *P; + *P = (*P)->DictNext; + break; + } + return Item; +} + +static inline VOID FspProcessBufferReuseEntry(HANDLE ProcessId, + FSP_PROCESS_BUFFER_LIST_ENTRY *BufferEntry) +{ + KIRQL Irql; + FSP_PROCESS_BUFFER_ITEM *Item; + + KeAcquireSpinLock(&ProcessBufferLock, &Irql); + + Item = FspProcessBufferLookupItemAtDpcLevel(ProcessId); + + if (0 != Item) + { + BufferEntry->Next = Item->BufferList; + Item->BufferList = BufferEntry; + } + + KeReleaseSpinLock(&ProcessBufferLock, Irql); + + if (0 == Item) + { + if (0 != BufferEntry->Buffer) + { + SIZE_T BufferSize = 0; + ZwFreeVirtualMemory(ZwCurrentProcess(), BufferEntry->Buffer, &BufferSize, MEM_RELEASE); + } + + FspFree(BufferEntry); + } +} + +NTSTATUS FspProcessBufferInitialize(VOID) +{ + KeInitializeSpinLock(&ProcessBufferLock); + + return PsSetCreateProcessNotifyRoutine(FspProcessBufferNotifyRoutine, FALSE); +} + +VOID FspProcessBufferFinalize(VOID) +{ + PsSetCreateProcessNotifyRoutine(FspProcessBufferNotifyRoutine, TRUE); +} + +static VOID FspProcessBufferNotifyRoutine(HANDLE ParentId, HANDLE ProcessId, BOOLEAN Create) +{ + if (!Create) + FspProcessBufferCollect(ProcessId); +} + +VOID FspProcessBufferCollect(HANDLE ProcessId) +{ + KIRQL Irql; + FSP_PROCESS_BUFFER_ITEM *Item = 0; + + KeAcquireSpinLock(&ProcessBufferLock, &Irql); + + Item = FspProcessBufferRemoveItemAtDpcLevel(ProcessId); + + KeReleaseSpinLock(&ProcessBufferLock, Irql); + + if (0 != Item) + { + DEBUGLOG("pid=%ld", (ULONG)(UINT_PTR)ProcessId); + + for (FSP_PROCESS_BUFFER_LIST_ENTRY *P = Item->BufferList, *Next; P; P = Next) + { + Next = P->Next; + FspFree(P); + } + + FspFree(Item); + } +} + +NTSTATUS FspProcessBufferAcquire(SIZE_T BufferSize, PVOID *PBufferCookie, PVOID *PBuffer) +{ + if (FspProcessBufferSizeMax >= BufferSize) + { + HANDLE ProcessId = SafeGetCurrentProcessId(); + KIRQL Irql; + FSP_PROCESS_BUFFER_ITEM *Item, *NewItem; + FSP_PROCESS_BUFFER_LIST_ENTRY *BufferEntry = 0; + BOOLEAN AllocNoReuse; + NTSTATUS Result; + + KeAcquireSpinLock(&ProcessBufferLock, &Irql); + + Item = FspProcessBufferLookupItemAtDpcLevel(ProcessId); + + if (0 != Item) + { + BufferEntry = Item->BufferList; + if (0 != BufferEntry) + Item->BufferList = BufferEntry->Next; + } + + AllocNoReuse = 0 == BufferEntry && + (0 != Item && FspProcessBufferCountMax <= Item->BufferCount); + + KeReleaseSpinLock(&ProcessBufferLock, Irql); + + if (AllocNoReuse) + goto alloc_no_reuse; + + if (0 == BufferEntry) + { + *PBufferCookie = 0; + *PBuffer = 0; + + BufferEntry = FspAllocNonPaged(sizeof *BufferEntry); + if (0 == BufferEntry) + return STATUS_INSUFFICIENT_RESOURCES; + RtlZeroMemory(BufferEntry, sizeof *BufferEntry); + + NewItem = FspAllocNonPaged(sizeof *NewItem); + if (0 == NewItem) + { + FspFree(BufferEntry); + return STATUS_INSUFFICIENT_RESOURCES; + } + RtlZeroMemory(NewItem, sizeof *NewItem); + + KeAcquireSpinLock(&ProcessBufferLock, &Irql); + + Item = FspProcessBufferLookupItemAtDpcLevel(ProcessId); + + if (0 == Item) + { + Item = NewItem; + NewItem = 0; + Item->BufferCount = 1; + Item->ProcessId = ProcessId; + FspProcessBufferAddItemAtDpcLevel(Item); + } + else if (FspProcessBufferCountMax > Item->BufferCount) + Item->BufferCount++; + else + AllocNoReuse = TRUE; + + KeReleaseSpinLock(&ProcessBufferLock, Irql); + + if (0 != NewItem) + FspFree(NewItem); + + if (AllocNoReuse) + { + FspFree(BufferEntry); + goto alloc_no_reuse; + } + } + + if (0 == BufferEntry->Buffer) + { + BufferSize = FspProcessBufferSizeMax; + Result = ZwAllocateVirtualMemory(ZwCurrentProcess(), + &BufferEntry->Buffer, 0, &BufferSize, MEM_COMMIT, PAGE_READWRITE); + if (!NT_SUCCESS(Result)) + { + /* failed to allocate actual buffer; reuse BufferEntry */ + FspProcessBufferReuseEntry(ProcessId, BufferEntry); + + return Result; + } + } + + *PBufferCookie = BufferEntry; + *PBuffer = BufferEntry->Buffer; + + return STATUS_SUCCESS; + } + else + { + alloc_no_reuse: + NTSTATUS Result; + + *PBufferCookie = 0; + Result = ZwAllocateVirtualMemory(ZwCurrentProcess(), + PBuffer, 0, &BufferSize, MEM_COMMIT, PAGE_READWRITE); + + return Result; + } +} + +VOID FspProcessBufferRelease(PVOID BufferCookie, PVOID Buffer) +{ + if (0 != BufferCookie) + { + HANDLE ProcessId = SafeGetCurrentProcessId(); + FSP_PROCESS_BUFFER_LIST_ENTRY *BufferEntry = BufferCookie; + + ASSERT(Buffer == BufferEntry->Buffer); + + FspProcessBufferReuseEntry(ProcessId, BufferEntry); + } + else + { + SIZE_T BufferSize = 0; + ZwFreeVirtualMemory(ZwCurrentProcess(), Buffer, &BufferSize, MEM_RELEASE); + } +} diff --git a/src/sys/read.c b/src/sys/read.c index 8666e52f..80fb07c1 100644 --- a/src/sys/read.c +++ b/src/sys/read.c @@ -44,6 +44,7 @@ enum { /* ReadNonCached */ RequestIrp = 0, + RequestCookie = 1, RequestSafeMdl = 1, RequestAddress = 2, RequestProcess = 3, @@ -346,41 +347,71 @@ NTSTATUS FspFsvolReadPrepare( { PAGED_CODE(); - NTSTATUS Result; - FSP_SAFE_MDL *SafeMdl = 0; - PVOID Address; - PEPROCESS Process; - - /* create a "safe" MDL if necessary */ - if (!FspSafeMdlCheck(Irp->MdlAddress)) + if (FspFsvolDeviceReadShouldUseProcessBuffer( + IoGetCurrentIrpStackLocation(Irp)->DeviceObject, Request->Req.Read.Length)) { - Result = FspSafeMdlCreate(Irp->MdlAddress, IoWriteAccess, &SafeMdl); + NTSTATUS Result; + PVOID Cookie; + PVOID Address; + PEPROCESS Process; + + if (0 == MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority)) + return STATUS_INSUFFICIENT_RESOURCES; /* something is seriously screwy! */ + + Result = FspProcessBufferAcquire(Request->Req.Read.Length, &Cookie, &Address); if (!NT_SUCCESS(Result)) return Result; - } - /* map the MDL into user-mode */ - Result = FspMapLockedPagesInUserMode( - 0 != SafeMdl ? SafeMdl->Mdl : Irp->MdlAddress, &Address, 0); - if (!NT_SUCCESS(Result)) + /* get a pointer to the current process so that we can release the buffer later */ + Process = PsGetCurrentProcess(); + ObReferenceObject(Process); + + Request->Req.Read.Address = (UINT64)(UINT_PTR)Address; + + FspIopRequestContext(Request, RequestCookie) = Cookie; + FspIopRequestContext(Request, RequestAddress) = Address; + FspIopRequestContext(Request, RequestProcess) = Process; + + return STATUS_SUCCESS; + } + else { - if (0 != SafeMdl) - FspSafeMdlDelete(SafeMdl); + NTSTATUS Result; + FSP_SAFE_MDL *SafeMdl = 0; + PVOID Address; + PEPROCESS Process; - return Result; + /* create a "safe" MDL if necessary */ + if (!FspSafeMdlCheck(Irp->MdlAddress)) + { + Result = FspSafeMdlCreate(Irp->MdlAddress, IoWriteAccess, &SafeMdl); + if (!NT_SUCCESS(Result)) + return Result; + } + + /* map the MDL into user-mode */ + Result = FspMapLockedPagesInUserMode( + 0 != SafeMdl ? SafeMdl->Mdl : Irp->MdlAddress, &Address, 0); + if (!NT_SUCCESS(Result)) + { + if (0 != SafeMdl) + FspSafeMdlDelete(SafeMdl); + + return Result; + } + + /* get a pointer to the current process so that we can unmap the address later */ + Process = PsGetCurrentProcess(); + ObReferenceObject(Process); + + Request->Req.Read.Address = (UINT64)(UINT_PTR)Address; + + FspIopRequestContext(Request, RequestSafeMdl) = SafeMdl; + FspIopRequestContext(Request, RequestAddress) = Address; + FspIopRequestContext(Request, RequestProcess) = Process; + + return STATUS_SUCCESS; } - - /* get a pointer to the current process so that we can unmap the address later */ - Process = PsGetCurrentProcess(); - ObReferenceObject(Process); - - Request->Req.Read.Address = (UINT64)(UINT_PTR)Address; - - FspIopRequestContext(Request, RequestSafeMdl) = SafeMdl; - FspIopRequestContext(Request, RequestAddress) = Address; - FspIopRequestContext(Request, RequestProcess) = Process; - - return STATUS_SUCCESS; } NTSTATUS FspFsvolReadComplete( @@ -400,15 +431,37 @@ NTSTATUS FspFsvolReadComplete( if (Response->IoStatus.Information > Request->Req.Read.Length) FSP_RETURN(Result = STATUS_INTERNAL_ERROR); - FSP_SAFE_MDL *SafeMdl = FspIopRequestContext(Request, RequestSafeMdl); + if (FspFsvolDeviceReadShouldUseProcessBuffer( + IrpSp->DeviceObject, Request->Req.Read.Length)) + { + PVOID Address = FspIopRequestContext(Request, RequestAddress); + PVOID SystemAddress = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority); + + ASSERT(0 != Address); + try + { + RtlCopyMemory(SystemAddress, Address, Response->IoStatus.Information); + } + except (EXCEPTION_EXECUTE_HANDLER) + { + Result = GetExceptionCode(); + Result = FsRtlIsNtstatusExpected(Result) ? STATUS_INVALID_USER_BUFFER : Result; + FSP_RETURN(); + } + } + else + { + FSP_SAFE_MDL *SafeMdl = FspIopRequestContext(Request, RequestSafeMdl); + + if (0 != SafeMdl) + FspSafeMdlCopyBack(SafeMdl); + } + PFILE_OBJECT FileObject = IrpSp->FileObject; LARGE_INTEGER ReadOffset = IrpSp->Parameters.Read.ByteOffset; BOOLEAN PagingIo = BooleanFlagOn(Irp->Flags, IRP_PAGING_IO); BOOLEAN SynchronousIo = BooleanFlagOn(FileObject->Flags, FO_SYNCHRONOUS_IO); - if (0 != SafeMdl) - FspSafeMdlCopyBack(SafeMdl); - /* if we are top-level */ if (0 == FspIrpTopFlags(Irp)) { @@ -442,29 +495,57 @@ static VOID FspFsvolReadNonCachedRequestFini(FSP_FSCTL_TRANSACT_REQ *Request, PV PAGED_CODE(); PIRP Irp = Context[RequestIrp]; - FSP_SAFE_MDL *SafeMdl = Context[RequestSafeMdl]; - PVOID Address = Context[RequestAddress]; - PEPROCESS Process = Context[RequestProcess]; - if (0 != Address) + if (0 != Irp && FspFsvolDeviceReadShouldUseProcessBuffer( + IoGetCurrentIrpStackLocation(Irp)->DeviceObject, Request->Req.Read.Length)) { - KAPC_STATE ApcState; - BOOLEAN Attach; + PVOID Cookie = Context[RequestCookie]; + PVOID Address = Context[RequestAddress]; + PEPROCESS Process = Context[RequestProcess]; - ASSERT(0 != Process); - Attach = Process != PsGetCurrentProcess(); + if (0 != Address) + { + KAPC_STATE ApcState; + BOOLEAN Attach; - if (Attach) - KeStackAttachProcess(Process, &ApcState); - MmUnmapLockedPages(Address, 0 != SafeMdl ? SafeMdl->Mdl : Irp->MdlAddress); - if (Attach) - KeUnstackDetachProcess(&ApcState); + ASSERT(0 != Process); + Attach = Process != PsGetCurrentProcess(); - ObDereferenceObject(Process); + if (Attach) + KeStackAttachProcess(Process, &ApcState); + FspProcessBufferRelease(Cookie, Address); + if (Attach) + KeUnstackDetachProcess(&ApcState); + + ObDereferenceObject(Process); + } } + else + { + FSP_SAFE_MDL *SafeMdl = Context[RequestSafeMdl]; + PVOID Address = Context[RequestAddress]; + PEPROCESS Process = Context[RequestProcess]; - if (0 != SafeMdl) - FspSafeMdlDelete(SafeMdl); + if (0 != Address) + { + KAPC_STATE ApcState; + BOOLEAN Attach; + + ASSERT(0 != Process); + Attach = Process != PsGetCurrentProcess(); + + if (Attach) + KeStackAttachProcess(Process, &ApcState); + MmUnmapLockedPages(Address, 0 != SafeMdl ? SafeMdl->Mdl : Irp->MdlAddress); + if (Attach) + KeUnstackDetachProcess(&ApcState); + + ObDereferenceObject(Process); + } + + if (0 != SafeMdl) + FspSafeMdlDelete(SafeMdl); + } if (0 != Irp) { diff --git a/src/sys/write.c b/src/sys/write.c index 9f700655..9b0fedfd 100644 --- a/src/sys/write.c +++ b/src/sys/write.c @@ -45,6 +45,7 @@ enum { /* WriteNonCached */ RequestIrp = 0, + RequestCookie = 1, RequestSafeMdl = 1, RequestAddress = 2, RequestProcess = 3, @@ -416,41 +417,87 @@ NTSTATUS FspFsvolWritePrepare( { PAGED_CODE(); - NTSTATUS Result; - FSP_SAFE_MDL *SafeMdl = 0; - PVOID Address; - PEPROCESS Process; - - /* create a "safe" MDL if necessary */ - if (!FspSafeMdlCheck(Irp->MdlAddress)) + if (FspFsvolDeviceWriteShouldUseProcessBuffer( + IoGetCurrentIrpStackLocation(Irp)->DeviceObject, Request->Req.Write.Length)) { - Result = FspSafeMdlCreate(Irp->MdlAddress, IoReadAccess, &SafeMdl); + NTSTATUS Result; + PVOID Cookie; + PVOID Address; + PEPROCESS Process; + PVOID SystemAddress = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority); + + if (0 == SystemAddress) + return STATUS_INSUFFICIENT_RESOURCES; /* something is seriously screwy! */ + + Result = FspProcessBufferAcquire(Request->Req.Write.Length, &Cookie, &Address); if (!NT_SUCCESS(Result)) return Result; - } - /* map the MDL into user-mode */ - Result = FspMapLockedPagesInUserMode( - 0 != SafeMdl ? SafeMdl->Mdl : Irp->MdlAddress, &Address, FspMvMdlMappingNoWrite); - if (!NT_SUCCESS(Result)) + ASSERT(0 != Address); + try + { + RtlCopyMemory(Address, SystemAddress, Request->Req.Write.Length); + } + except (EXCEPTION_EXECUTE_HANDLER) + { + Result = GetExceptionCode(); + Result = FsRtlIsNtstatusExpected(Result) ? STATUS_INVALID_USER_BUFFER : Result; + + FspProcessBufferRelease(Cookie, Address); + + return Result; + } + + /* get a pointer to the current process so that we can release the buffer later */ + Process = PsGetCurrentProcess(); + ObReferenceObject(Process); + + Request->Req.Write.Address = (UINT64)(UINT_PTR)Address; + + FspIopRequestContext(Request, RequestCookie) = Cookie; + FspIopRequestContext(Request, RequestAddress) = Address; + FspIopRequestContext(Request, RequestProcess) = Process; + + return STATUS_SUCCESS; + } + else { - if (0 != SafeMdl) - FspSafeMdlDelete(SafeMdl); + NTSTATUS Result; + FSP_SAFE_MDL *SafeMdl = 0; + PVOID Address; + PEPROCESS Process; - return Result; + /* create a "safe" MDL if necessary */ + if (!FspSafeMdlCheck(Irp->MdlAddress)) + { + Result = FspSafeMdlCreate(Irp->MdlAddress, IoReadAccess, &SafeMdl); + if (!NT_SUCCESS(Result)) + return Result; + } + + /* map the MDL into user-mode */ + Result = FspMapLockedPagesInUserMode( + 0 != SafeMdl ? SafeMdl->Mdl : Irp->MdlAddress, &Address, FspMvMdlMappingNoWrite); + if (!NT_SUCCESS(Result)) + { + if (0 != SafeMdl) + FspSafeMdlDelete(SafeMdl); + + return Result; + } + + /* get a pointer to the current process so that we can unmap the address later */ + Process = PsGetCurrentProcess(); + ObReferenceObject(Process); + + Request->Req.Write.Address = (UINT64)(UINT_PTR)Address; + + FspIopRequestContext(Request, RequestSafeMdl) = SafeMdl; + FspIopRequestContext(Request, RequestAddress) = Address; + FspIopRequestContext(Request, RequestProcess) = Process; + + return STATUS_SUCCESS; } - - /* get a pointer to the current process so that we can unmap the address later */ - Process = PsGetCurrentProcess(); - ObReferenceObject(Process); - - Request->Req.Write.Address = (UINT64)(UINT_PTR)Address; - - FspIopRequestContext(Request, RequestSafeMdl) = SafeMdl; - FspIopRequestContext(Request, RequestAddress) = Address; - FspIopRequestContext(Request, RequestProcess) = Process; - - return STATUS_SUCCESS; } NTSTATUS FspFsvolWriteComplete( @@ -524,29 +571,57 @@ static VOID FspFsvolWriteNonCachedRequestFini(FSP_FSCTL_TRANSACT_REQ *Request, P PAGED_CODE(); PIRP Irp = Context[RequestIrp]; - FSP_SAFE_MDL *SafeMdl = Context[RequestSafeMdl]; - PVOID Address = Context[RequestAddress]; - PEPROCESS Process = Context[RequestProcess]; - if (0 != Address) + if (0 != Irp && FspFsvolDeviceWriteShouldUseProcessBuffer( + IoGetCurrentIrpStackLocation(Irp)->DeviceObject, Request->Req.Write.Length)) { - KAPC_STATE ApcState; - BOOLEAN Attach; + PVOID Cookie = Context[RequestCookie]; + PVOID Address = Context[RequestAddress]; + PEPROCESS Process = Context[RequestProcess]; - ASSERT(0 != Process); - Attach = Process != PsGetCurrentProcess(); + if (0 != Address) + { + KAPC_STATE ApcState; + BOOLEAN Attach; - if (Attach) - KeStackAttachProcess(Process, &ApcState); - MmUnmapLockedPages(Address, 0 != SafeMdl ? SafeMdl->Mdl : Irp->MdlAddress); - if (Attach) - KeUnstackDetachProcess(&ApcState); + ASSERT(0 != Process); + Attach = Process != PsGetCurrentProcess(); - ObDereferenceObject(Process); + if (Attach) + KeStackAttachProcess(Process, &ApcState); + FspProcessBufferRelease(Cookie, Address); + if (Attach) + KeUnstackDetachProcess(&ApcState); + + ObDereferenceObject(Process); + } } + else + { + FSP_SAFE_MDL *SafeMdl = Context[RequestSafeMdl]; + PVOID Address = Context[RequestAddress]; + PEPROCESS Process = Context[RequestProcess]; - if (0 != SafeMdl) - FspSafeMdlDelete(SafeMdl); + if (0 != Address) + { + KAPC_STATE ApcState; + BOOLEAN Attach; + + ASSERT(0 != Process); + Attach = Process != PsGetCurrentProcess(); + + if (Attach) + KeStackAttachProcess(Process, &ApcState); + MmUnmapLockedPages(Address, 0 != SafeMdl ? SafeMdl->Mdl : Irp->MdlAddress); + if (Attach) + KeUnstackDetachProcess(&ApcState); + + ObDereferenceObject(Process); + } + + if (0 != SafeMdl) + FspSafeMdlDelete(SafeMdl); + } if (0 != Irp) { diff --git a/tst/memfs/memfs.cpp b/tst/memfs/memfs.cpp index 3e6c33ef..697893f8 100644 --- a/tst/memfs/memfs.cpp +++ b/tst/memfs/memfs.cpp @@ -45,9 +45,11 @@ FSP_FSCTL_STATIC_ASSERT(MEMFS_MAX_PATH > MAX_PATH, /* * Define the DEBUG_BUFFER_CHECK macro on Windows 8 or above. This includes * a check for the Write buffer to ensure that it is read-only. + * + * Since ProcessBuffer support in the FSD, this is no longer a guarantee. */ #if !defined(NDEBUG) -#define DEBUG_BUFFER_CHECK +//#define DEBUG_BUFFER_CHECK #endif #define MEMFS_SECTOR_SIZE 512