mirror of
https://github.com/winfsp/winfsp.git
synced 2025-07-03 17:32:57 -05:00
sys: initial ProcessBuffer implementation
This commit is contained in:
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);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user