mirror of
https://github.com/winfsp/winfsp.git
synced 2025-04-23 00:43:00 -05:00
sys: initial ProcessBuffer implementation
This commit is contained in:
parent
441c45c77f
commit
4b43cc590f
@ -174,6 +174,7 @@
|
|||||||
<ClCompile Include="..\..\src\sys\lockctl.c" />
|
<ClCompile Include="..\..\src\sys\lockctl.c" />
|
||||||
<ClCompile Include="..\..\src\sys\meta.c" />
|
<ClCompile Include="..\..\src\sys\meta.c" />
|
||||||
<ClCompile Include="..\..\src\sys\name.c" />
|
<ClCompile Include="..\..\src\sys\name.c" />
|
||||||
|
<ClCompile Include="..\..\src\sys\psbuffer.c" />
|
||||||
<ClCompile Include="..\..\src\sys\read.c" />
|
<ClCompile Include="..\..\src\sys\read.c" />
|
||||||
<ClCompile Include="..\..\src\sys\security.c" />
|
<ClCompile Include="..\..\src\sys\security.c" />
|
||||||
<ClCompile Include="..\..\src\sys\shutdown.c" />
|
<ClCompile Include="..\..\src\sys\shutdown.c" />
|
||||||
|
@ -98,6 +98,9 @@
|
|||||||
<ClCompile Include="..\..\src\sys\statistics.c">
|
<ClCompile Include="..\..\src\sys\statistics.c">
|
||||||
<Filter>Source</Filter>
|
<Filter>Source</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\..\src\sys\psbuffer.c">
|
||||||
|
<Filter>Source</Filter>
|
||||||
|
</ClCompile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="..\..\src\sys\driver.h">
|
<ClInclude Include="..\..\src\sys\driver.h">
|
||||||
|
@ -148,7 +148,8 @@ typedef struct
|
|||||||
/* kernel-mode flags */
|
/* kernel-mode flags */
|
||||||
UINT32 PostCleanupWhenModifiedOnly:1; /* post Cleanup when a file was modified/deleted */
|
UINT32 PostCleanupWhenModifiedOnly:1; /* post Cleanup when a file was modified/deleted */
|
||||||
UINT32 PassQueryDirectoryPattern:1; /* pass Pattern during QueryDirectory operations */
|
UINT32 PassQueryDirectoryPattern:1; /* pass Pattern during QueryDirectory operations */
|
||||||
UINT32 KmReservedFlags:4;
|
UINT32 AlwaysUseDoubleBuffering:1;
|
||||||
|
UINT32 KmReservedFlags:3;
|
||||||
/* user-mode flags */
|
/* user-mode flags */
|
||||||
UINT32 UmFileContextIsUserContext2:1; /* user mode: FileContext parameter is UserContext2 */
|
UINT32 UmFileContextIsUserContext2:1; /* user mode: FileContext parameter is UserContext2 */
|
||||||
UINT32 UmFileContextIsFullContext:1; /* user mode: FileContext parameter is FullContext */
|
UINT32 UmFileContextIsFullContext:1; /* user mode: FileContext parameter is FullContext */
|
||||||
|
@ -78,6 +78,7 @@ enum
|
|||||||
{
|
{
|
||||||
/* QueryDirectory */
|
/* QueryDirectory */
|
||||||
RequestIrp = 0,
|
RequestIrp = 0,
|
||||||
|
RequestCookie = 1,
|
||||||
RequestMdl = 1,
|
RequestMdl = 1,
|
||||||
RequestAddress = 2,
|
RequestAddress = 2,
|
||||||
RequestProcess = 3,
|
RequestProcess = 3,
|
||||||
@ -833,6 +834,32 @@ NTSTATUS FspFsvolDirectoryControlPrepare(
|
|||||||
{
|
{
|
||||||
PAGED_CODE();
|
PAGED_CODE();
|
||||||
|
|
||||||
|
if (FspFsvolDeviceQueryDirectoryShouldUseProcessBuffer(
|
||||||
|
IoGetCurrentIrpStackLocation(Irp)->DeviceObject, Request->Req.QueryDirectory.Length))
|
||||||
|
{
|
||||||
|
NTSTATUS Result;
|
||||||
|
PVOID Cookie;
|
||||||
|
PVOID Address;
|
||||||
|
PEPROCESS Process;
|
||||||
|
|
||||||
|
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;
|
NTSTATUS Result;
|
||||||
PMDL Mdl = 0;
|
PMDL Mdl = 0;
|
||||||
PVOID Address;
|
PVOID Address;
|
||||||
@ -868,6 +895,7 @@ NTSTATUS FspFsvolDirectoryControlPrepare(
|
|||||||
FspIopRequestContext(Request, RequestProcess) = Process;
|
FspIopRequestContext(Request, RequestProcess) = Process;
|
||||||
|
|
||||||
return STATUS_SUCCESS;
|
return STATUS_SUCCESS;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NTSTATUS FspFsvolDirectoryControlComplete(
|
NTSTATUS FspFsvolDirectoryControlComplete(
|
||||||
@ -910,6 +938,24 @@ NTSTATUS FspFsvolDirectoryControlComplete(
|
|||||||
|
|
||||||
if (FspFsctlTransactQueryDirectoryKind == Request->Kind)
|
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);
|
DirInfoChangeNumber = FspFileNodeDirInfoChangeNumber(FileNode);
|
||||||
Request->Kind = FspFsctlTransactReservedKind;
|
Request->Kind = FspFsctlTransactReservedKind;
|
||||||
FspIopResetRequest(Request, 0);
|
FspIopResetRequest(Request, 0);
|
||||||
@ -1010,6 +1056,33 @@ static VOID FspFsvolQueryDirectoryRequestFini(FSP_FSCTL_TRANSACT_REQ *Request, P
|
|||||||
PAGED_CODE();
|
PAGED_CODE();
|
||||||
|
|
||||||
PIRP Irp = Context[RequestIrp];
|
PIRP Irp = Context[RequestIrp];
|
||||||
|
|
||||||
|
if (0 != Irp && FspFsvolDeviceQueryDirectoryShouldUseProcessBuffer(
|
||||||
|
IoGetCurrentIrpStackLocation(Irp)->DeviceObject, Request->Req.QueryDirectory.Length))
|
||||||
|
{
|
||||||
|
PVOID Cookie = Context[RequestCookie];
|
||||||
|
PVOID Address = Context[RequestAddress];
|
||||||
|
PEPROCESS Process = Context[RequestProcess];
|
||||||
|
|
||||||
|
if (0 != Address)
|
||||||
|
{
|
||||||
|
KAPC_STATE ApcState;
|
||||||
|
BOOLEAN Attach;
|
||||||
|
|
||||||
|
ASSERT(0 != Process);
|
||||||
|
Attach = Process != PsGetCurrentProcess();
|
||||||
|
|
||||||
|
if (Attach)
|
||||||
|
KeStackAttachProcess(Process, &ApcState);
|
||||||
|
FspProcessBufferRelease(Cookie, Address);
|
||||||
|
if (Attach)
|
||||||
|
KeUnstackDetachProcess(&ApcState);
|
||||||
|
|
||||||
|
ObDereferenceObject(Process);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
PMDL Mdl = Context[RequestMdl];
|
PMDL Mdl = Context[RequestMdl];
|
||||||
PVOID Address = Context[RequestAddress];
|
PVOID Address = Context[RequestAddress];
|
||||||
PEPROCESS Process = Context[RequestProcess];
|
PEPROCESS Process = Context[RequestProcess];
|
||||||
@ -1033,6 +1106,7 @@ static VOID FspFsvolQueryDirectoryRequestFini(FSP_FSCTL_TRANSACT_REQ *Request, P
|
|||||||
|
|
||||||
if (0 != Mdl)
|
if (0 != Mdl)
|
||||||
IoFreeMdl(Mdl);
|
IoFreeMdl(Mdl);
|
||||||
|
}
|
||||||
|
|
||||||
if (0 != Irp)
|
if (0 != Irp)
|
||||||
{
|
{
|
||||||
|
@ -46,6 +46,10 @@ NTSTATUS DriverEntry(
|
|||||||
|
|
||||||
FspDriverMultiVersionInitialize();
|
FspDriverMultiVersionInitialize();
|
||||||
|
|
||||||
|
Result = FspProcessBufferInitialize();
|
||||||
|
if (!NT_SUCCESS(Result))
|
||||||
|
FSP_RETURN();
|
||||||
|
|
||||||
FspDriverObject = DriverObject;
|
FspDriverObject = DriverObject;
|
||||||
ExInitializeResourceLite(&FspDeviceGlobalResource);
|
ExInitializeResourceLite(&FspDeviceGlobalResource);
|
||||||
|
|
||||||
@ -59,14 +63,21 @@ NTSTATUS DriverEntry(
|
|||||||
&DeviceSddl, &FspFsctlDeviceClassGuid,
|
&DeviceSddl, &FspFsctlDeviceClassGuid,
|
||||||
&FspFsctlDiskDeviceObject);
|
&FspFsctlDiskDeviceObject);
|
||||||
if (!NT_SUCCESS(Result))
|
if (!NT_SUCCESS(Result))
|
||||||
|
{
|
||||||
|
FspProcessBufferFinalize();
|
||||||
FSP_RETURN();
|
FSP_RETURN();
|
||||||
|
}
|
||||||
RtlInitUnicodeString(&DeviceName, L"\\Device\\" FSP_FSCTL_NET_DEVICE_NAME);
|
RtlInitUnicodeString(&DeviceName, L"\\Device\\" FSP_FSCTL_NET_DEVICE_NAME);
|
||||||
Result = FspDeviceCreateSecure(FspFsctlDeviceExtensionKind, 0,
|
Result = FspDeviceCreateSecure(FspFsctlDeviceExtensionKind, 0,
|
||||||
&DeviceName, FILE_DEVICE_NETWORK_FILE_SYSTEM, FILE_DEVICE_SECURE_OPEN,
|
&DeviceName, FILE_DEVICE_NETWORK_FILE_SYSTEM, FILE_DEVICE_SECURE_OPEN,
|
||||||
&DeviceSddl, &FspFsctlDeviceClassGuid,
|
&DeviceSddl, &FspFsctlDeviceClassGuid,
|
||||||
&FspFsctlNetDeviceObject);
|
&FspFsctlNetDeviceObject);
|
||||||
if (!NT_SUCCESS(Result))
|
if (!NT_SUCCESS(Result))
|
||||||
FSP_RETURN(FspDeviceDelete(FspFsctlDiskDeviceObject));
|
{
|
||||||
|
FspDeviceDelete(FspFsctlDiskDeviceObject);
|
||||||
|
FspProcessBufferFinalize();
|
||||||
|
FSP_RETURN();
|
||||||
|
}
|
||||||
Result = FspDeviceInitialize(FspFsctlDiskDeviceObject);
|
Result = FspDeviceInitialize(FspFsctlDiskDeviceObject);
|
||||||
ASSERT(STATUS_SUCCESS == Result);
|
ASSERT(STATUS_SUCCESS == Result);
|
||||||
Result = FspDeviceInitialize(FspFsctlNetDeviceObject);
|
Result = FspDeviceInitialize(FspFsctlNetDeviceObject);
|
||||||
@ -207,6 +218,8 @@ VOID FspUnload(
|
|||||||
ExDeleteResourceLite(&FspDeviceGlobalResource);
|
ExDeleteResourceLite(&FspDeviceGlobalResource);
|
||||||
FspDriverObject = 0;
|
FspDriverObject = 0;
|
||||||
|
|
||||||
|
FspProcessBufferFinalize();
|
||||||
|
|
||||||
#pragma prefast(suppress:28175, "We are in DriverUnload: ok to access DriverName")
|
#pragma prefast(suppress:28175, "We are in DriverUnload: ok to access DriverName")
|
||||||
FSP_LEAVE_VOID("DriverName=\"%wZ\"",
|
FSP_LEAVE_VOID("DriverName=\"%wZ\"",
|
||||||
&DriverObject->DriverName);
|
&DriverObject->DriverName);
|
||||||
|
@ -601,6 +601,14 @@ VOID FspIrpHookReset(PIRP Irp);
|
|||||||
PVOID FspIrpHookContext(PVOID Context);
|
PVOID FspIrpHookContext(PVOID Context);
|
||||||
NTSTATUS FspIrpHookNext(PDEVICE_OBJECT DeviceObject, PIRP Irp, 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 */
|
/* IRP context */
|
||||||
#define FspIrpTimestampInfinity ((ULONG)-1L)
|
#define FspIrpTimestampInfinity ((ULONG)-1L)
|
||||||
#define FspIrpTimestamp(Irp) \
|
#define FspIrpTimestamp(Irp) \
|
||||||
@ -1048,6 +1056,23 @@ FSP_FSVOL_DEVICE_EXTENSION *FspFsvolDeviceExtension(PDEVICE_OBJECT DeviceObject)
|
|||||||
ASSERT(FspFsvolDeviceExtensionKind == ((FSP_DEVICE_EXTENSION *)DeviceObject->DeviceExtension)->Kind);
|
ASSERT(FspFsvolDeviceExtensionKind == ((FSP_DEVICE_EXTENSION *)DeviceObject->DeviceExtension)->Kind);
|
||||||
return DeviceObject->DeviceExtension;
|
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,
|
NTSTATUS FspDeviceCreateSecure(UINT32 Kind, ULONG ExtraSize,
|
||||||
PUNICODE_STRING DeviceName, DEVICE_TYPE DeviceType, ULONG DeviceCharacteristics,
|
PUNICODE_STRING DeviceName, DEVICE_TYPE DeviceType, ULONG DeviceCharacteristics,
|
||||||
PUNICODE_STRING DeviceSddl, LPCGUID DeviceClassGuid,
|
PUNICODE_STRING DeviceSddl, LPCGUID DeviceClassGuid,
|
||||||
|
280
src/sys/psbuffer.c
Normal file
280
src/sys/psbuffer.c
Normal file
@ -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 <sys/driver.h>
|
||||||
|
|
||||||
|
#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);
|
||||||
|
}
|
||||||
|
}
|
@ -44,6 +44,7 @@ enum
|
|||||||
{
|
{
|
||||||
/* ReadNonCached */
|
/* ReadNonCached */
|
||||||
RequestIrp = 0,
|
RequestIrp = 0,
|
||||||
|
RequestCookie = 1,
|
||||||
RequestSafeMdl = 1,
|
RequestSafeMdl = 1,
|
||||||
RequestAddress = 2,
|
RequestAddress = 2,
|
||||||
RequestProcess = 3,
|
RequestProcess = 3,
|
||||||
@ -346,6 +347,35 @@ NTSTATUS FspFsvolReadPrepare(
|
|||||||
{
|
{
|
||||||
PAGED_CODE();
|
PAGED_CODE();
|
||||||
|
|
||||||
|
if (FspFsvolDeviceReadShouldUseProcessBuffer(
|
||||||
|
IoGetCurrentIrpStackLocation(Irp)->DeviceObject, Request->Req.Read.Length))
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
|
||||||
|
/* 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
|
||||||
|
{
|
||||||
NTSTATUS Result;
|
NTSTATUS Result;
|
||||||
FSP_SAFE_MDL *SafeMdl = 0;
|
FSP_SAFE_MDL *SafeMdl = 0;
|
||||||
PVOID Address;
|
PVOID Address;
|
||||||
@ -381,6 +411,7 @@ NTSTATUS FspFsvolReadPrepare(
|
|||||||
FspIopRequestContext(Request, RequestProcess) = Process;
|
FspIopRequestContext(Request, RequestProcess) = Process;
|
||||||
|
|
||||||
return STATUS_SUCCESS;
|
return STATUS_SUCCESS;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NTSTATUS FspFsvolReadComplete(
|
NTSTATUS FspFsvolReadComplete(
|
||||||
@ -400,15 +431,37 @@ NTSTATUS FspFsvolReadComplete(
|
|||||||
if (Response->IoStatus.Information > Request->Req.Read.Length)
|
if (Response->IoStatus.Information > Request->Req.Read.Length)
|
||||||
FSP_RETURN(Result = STATUS_INTERNAL_ERROR);
|
FSP_RETURN(Result = STATUS_INTERNAL_ERROR);
|
||||||
|
|
||||||
|
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);
|
FSP_SAFE_MDL *SafeMdl = FspIopRequestContext(Request, RequestSafeMdl);
|
||||||
|
|
||||||
|
if (0 != SafeMdl)
|
||||||
|
FspSafeMdlCopyBack(SafeMdl);
|
||||||
|
}
|
||||||
|
|
||||||
PFILE_OBJECT FileObject = IrpSp->FileObject;
|
PFILE_OBJECT FileObject = IrpSp->FileObject;
|
||||||
LARGE_INTEGER ReadOffset = IrpSp->Parameters.Read.ByteOffset;
|
LARGE_INTEGER ReadOffset = IrpSp->Parameters.Read.ByteOffset;
|
||||||
BOOLEAN PagingIo = BooleanFlagOn(Irp->Flags, IRP_PAGING_IO);
|
BOOLEAN PagingIo = BooleanFlagOn(Irp->Flags, IRP_PAGING_IO);
|
||||||
BOOLEAN SynchronousIo = BooleanFlagOn(FileObject->Flags, FO_SYNCHRONOUS_IO);
|
BOOLEAN SynchronousIo = BooleanFlagOn(FileObject->Flags, FO_SYNCHRONOUS_IO);
|
||||||
|
|
||||||
if (0 != SafeMdl)
|
|
||||||
FspSafeMdlCopyBack(SafeMdl);
|
|
||||||
|
|
||||||
/* if we are top-level */
|
/* if we are top-level */
|
||||||
if (0 == FspIrpTopFlags(Irp))
|
if (0 == FspIrpTopFlags(Irp))
|
||||||
{
|
{
|
||||||
@ -442,6 +495,33 @@ static VOID FspFsvolReadNonCachedRequestFini(FSP_FSCTL_TRANSACT_REQ *Request, PV
|
|||||||
PAGED_CODE();
|
PAGED_CODE();
|
||||||
|
|
||||||
PIRP Irp = Context[RequestIrp];
|
PIRP Irp = Context[RequestIrp];
|
||||||
|
|
||||||
|
if (0 != Irp && FspFsvolDeviceReadShouldUseProcessBuffer(
|
||||||
|
IoGetCurrentIrpStackLocation(Irp)->DeviceObject, Request->Req.Read.Length))
|
||||||
|
{
|
||||||
|
PVOID Cookie = Context[RequestCookie];
|
||||||
|
PVOID Address = Context[RequestAddress];
|
||||||
|
PEPROCESS Process = Context[RequestProcess];
|
||||||
|
|
||||||
|
if (0 != Address)
|
||||||
|
{
|
||||||
|
KAPC_STATE ApcState;
|
||||||
|
BOOLEAN Attach;
|
||||||
|
|
||||||
|
ASSERT(0 != Process);
|
||||||
|
Attach = Process != PsGetCurrentProcess();
|
||||||
|
|
||||||
|
if (Attach)
|
||||||
|
KeStackAttachProcess(Process, &ApcState);
|
||||||
|
FspProcessBufferRelease(Cookie, Address);
|
||||||
|
if (Attach)
|
||||||
|
KeUnstackDetachProcess(&ApcState);
|
||||||
|
|
||||||
|
ObDereferenceObject(Process);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
FSP_SAFE_MDL *SafeMdl = Context[RequestSafeMdl];
|
FSP_SAFE_MDL *SafeMdl = Context[RequestSafeMdl];
|
||||||
PVOID Address = Context[RequestAddress];
|
PVOID Address = Context[RequestAddress];
|
||||||
PEPROCESS Process = Context[RequestProcess];
|
PEPROCESS Process = Context[RequestProcess];
|
||||||
@ -465,6 +545,7 @@ static VOID FspFsvolReadNonCachedRequestFini(FSP_FSCTL_TRANSACT_REQ *Request, PV
|
|||||||
|
|
||||||
if (0 != SafeMdl)
|
if (0 != SafeMdl)
|
||||||
FspSafeMdlDelete(SafeMdl);
|
FspSafeMdlDelete(SafeMdl);
|
||||||
|
}
|
||||||
|
|
||||||
if (0 != Irp)
|
if (0 != Irp)
|
||||||
{
|
{
|
||||||
|
@ -45,6 +45,7 @@ enum
|
|||||||
{
|
{
|
||||||
/* WriteNonCached */
|
/* WriteNonCached */
|
||||||
RequestIrp = 0,
|
RequestIrp = 0,
|
||||||
|
RequestCookie = 1,
|
||||||
RequestSafeMdl = 1,
|
RequestSafeMdl = 1,
|
||||||
RequestAddress = 2,
|
RequestAddress = 2,
|
||||||
RequestProcess = 3,
|
RequestProcess = 3,
|
||||||
@ -416,6 +417,51 @@ NTSTATUS FspFsvolWritePrepare(
|
|||||||
{
|
{
|
||||||
PAGED_CODE();
|
PAGED_CODE();
|
||||||
|
|
||||||
|
if (FspFsvolDeviceWriteShouldUseProcessBuffer(
|
||||||
|
IoGetCurrentIrpStackLocation(Irp)->DeviceObject, Request->Req.Write.Length))
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
|
||||||
|
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
|
||||||
|
{
|
||||||
NTSTATUS Result;
|
NTSTATUS Result;
|
||||||
FSP_SAFE_MDL *SafeMdl = 0;
|
FSP_SAFE_MDL *SafeMdl = 0;
|
||||||
PVOID Address;
|
PVOID Address;
|
||||||
@ -451,6 +497,7 @@ NTSTATUS FspFsvolWritePrepare(
|
|||||||
FspIopRequestContext(Request, RequestProcess) = Process;
|
FspIopRequestContext(Request, RequestProcess) = Process;
|
||||||
|
|
||||||
return STATUS_SUCCESS;
|
return STATUS_SUCCESS;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NTSTATUS FspFsvolWriteComplete(
|
NTSTATUS FspFsvolWriteComplete(
|
||||||
@ -524,6 +571,33 @@ static VOID FspFsvolWriteNonCachedRequestFini(FSP_FSCTL_TRANSACT_REQ *Request, P
|
|||||||
PAGED_CODE();
|
PAGED_CODE();
|
||||||
|
|
||||||
PIRP Irp = Context[RequestIrp];
|
PIRP Irp = Context[RequestIrp];
|
||||||
|
|
||||||
|
if (0 != Irp && FspFsvolDeviceWriteShouldUseProcessBuffer(
|
||||||
|
IoGetCurrentIrpStackLocation(Irp)->DeviceObject, Request->Req.Write.Length))
|
||||||
|
{
|
||||||
|
PVOID Cookie = Context[RequestCookie];
|
||||||
|
PVOID Address = Context[RequestAddress];
|
||||||
|
PEPROCESS Process = Context[RequestProcess];
|
||||||
|
|
||||||
|
if (0 != Address)
|
||||||
|
{
|
||||||
|
KAPC_STATE ApcState;
|
||||||
|
BOOLEAN Attach;
|
||||||
|
|
||||||
|
ASSERT(0 != Process);
|
||||||
|
Attach = Process != PsGetCurrentProcess();
|
||||||
|
|
||||||
|
if (Attach)
|
||||||
|
KeStackAttachProcess(Process, &ApcState);
|
||||||
|
FspProcessBufferRelease(Cookie, Address);
|
||||||
|
if (Attach)
|
||||||
|
KeUnstackDetachProcess(&ApcState);
|
||||||
|
|
||||||
|
ObDereferenceObject(Process);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
FSP_SAFE_MDL *SafeMdl = Context[RequestSafeMdl];
|
FSP_SAFE_MDL *SafeMdl = Context[RequestSafeMdl];
|
||||||
PVOID Address = Context[RequestAddress];
|
PVOID Address = Context[RequestAddress];
|
||||||
PEPROCESS Process = Context[RequestProcess];
|
PEPROCESS Process = Context[RequestProcess];
|
||||||
@ -547,6 +621,7 @@ static VOID FspFsvolWriteNonCachedRequestFini(FSP_FSCTL_TRANSACT_REQ *Request, P
|
|||||||
|
|
||||||
if (0 != SafeMdl)
|
if (0 != SafeMdl)
|
||||||
FspSafeMdlDelete(SafeMdl);
|
FspSafeMdlDelete(SafeMdl);
|
||||||
|
}
|
||||||
|
|
||||||
if (0 != Irp)
|
if (0 != Irp)
|
||||||
{
|
{
|
||||||
|
@ -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
|
* 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.
|
* 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)
|
#if !defined(NDEBUG)
|
||||||
#define DEBUG_BUFFER_CHECK
|
//#define DEBUG_BUFFER_CHECK
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define MEMFS_SECTOR_SIZE 512
|
#define MEMFS_SECTOR_SIZE 512
|
||||||
|
Loading…
x
Reference in New Issue
Block a user