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