mirror of
https://github.com/winfsp/winfsp.git
synced 2025-04-24 09:23:37 -05:00
- FspFileSystemAcquireDirectoryBufferEx takes hint for initial capacity. - Buffer allocation strategy has been improved to minimize reallocation. - Quick sort of directory entries now implements median of three partitioning. This improves performance of sorting already sorted data.
443 lines
13 KiB
C
443 lines
13 KiB
C
/**
|
|
* @file dll/dirbuf.c
|
|
*
|
|
* @copyright 2015-2022 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 software
|
|
* in accordance with the commercial license agreement provided in
|
|
* conjunction with the software. The terms and conditions of any such
|
|
* commercial license agreement shall govern, supersede, and render
|
|
* ineffective any application of the GPLv3 license to this software,
|
|
* notwithstanding of any reference thereto in the software or
|
|
* associated repository.
|
|
*/
|
|
|
|
#include <dll/library.h>
|
|
|
|
#define FspFileSystemDirectoryBufferLoBound (256)
|
|
#define FspFileSystemDirectoryBufferHiBound (1024 * 1024)
|
|
#define FspFileSystemDirectoryBufferLoFactor (4)
|
|
#define FspFileSystemDirectoryBufferHiFactor (2)
|
|
|
|
#define RETURN(R, B) \
|
|
do \
|
|
{ \
|
|
if (0 != PResult) \
|
|
*PResult = R; \
|
|
return B; \
|
|
} while (0,0)
|
|
|
|
typedef struct
|
|
{
|
|
SRWLOCK Lock;
|
|
ULONG InitialCapacity, Capacity, LoMark, HiMark;
|
|
PUINT8 Buffer;
|
|
} FSP_FILE_SYSTEM_DIRECTORY_BUFFER;
|
|
|
|
static int FspFileSystemDirectoryBufferFileNameCmp(PWSTR a, int alen, PWSTR b, int blen)
|
|
{
|
|
int len, res;
|
|
|
|
if (-1 == alen)
|
|
alen = (int)lstrlenW(a);
|
|
if (-1 == blen)
|
|
blen = (int)lstrlenW(b);
|
|
|
|
len = alen < blen ? alen : blen;
|
|
|
|
/* order "." and ".." first */
|
|
switch (alen)
|
|
{
|
|
case 1:
|
|
if (L'.' == a[0])
|
|
a = L"\1";
|
|
break;
|
|
case 2:
|
|
if (L'.' == a[0] && L'.' == a[1])
|
|
a = L"\1\1";
|
|
break;
|
|
}
|
|
|
|
/* order "." and ".." first */
|
|
switch (blen)
|
|
{
|
|
case 1:
|
|
if (L'.' == b[0])
|
|
b = L"\1";
|
|
break;
|
|
case 2:
|
|
if (L'.' == b[0] && L'.' == b[1])
|
|
b = L"\1\1";
|
|
break;
|
|
}
|
|
|
|
res = invariant_wcsncmp(a, b, len);
|
|
|
|
if (0 == res)
|
|
res = alen - blen;
|
|
|
|
return res;
|
|
}
|
|
|
|
/*
|
|
* Binary search
|
|
* "I wish I had the standard library!"
|
|
*/
|
|
static BOOLEAN FspFileSystemSearchDirectoryBuffer(FSP_FILE_SYSTEM_DIRECTORY_BUFFER *DirBuffer,
|
|
PWSTR Marker, int MarkerLen, PULONG PIndexNum)
|
|
{
|
|
PULONG Index = (PULONG)(DirBuffer->Buffer + DirBuffer->HiMark);
|
|
ULONG Count = (DirBuffer->Capacity - DirBuffer->HiMark) / sizeof(ULONG);
|
|
FSP_FSCTL_DIR_INFO *DirInfo;
|
|
int Lo = 0, Hi = Count - 1, Mi;
|
|
int CmpResult;
|
|
|
|
while (Lo <= Hi)
|
|
{
|
|
Mi = (unsigned)(Lo + Hi) >> 1;
|
|
|
|
DirInfo = (PVOID)(DirBuffer->Buffer + Index[Mi]);
|
|
CmpResult = FspFileSystemDirectoryBufferFileNameCmp(
|
|
DirInfo->FileNameBuf, (DirInfo->Size - sizeof *DirInfo) / sizeof(WCHAR),
|
|
Marker, MarkerLen);
|
|
|
|
if (0 > CmpResult)
|
|
Lo = Mi + 1;
|
|
else if (0 < CmpResult)
|
|
Hi = Mi - 1;
|
|
else
|
|
{
|
|
*PIndexNum = Mi;
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
*PIndexNum = Hi;
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* Quick sort
|
|
* "I wish I had the standard library!"
|
|
*
|
|
* Based on Sedgewick's Algorithms in C++; multiple editions.
|
|
*
|
|
* Implements a non-recursive quicksort with tail-end recursion eliminated
|
|
* and median-of-three partitioning.
|
|
*/
|
|
|
|
#define less(a, b) FspFileSystemDirectoryBufferLess(Buffer, a, b)
|
|
#define exch(a, b) { ULONG t = a; a = b; b = t; }
|
|
#define compexch(a, b) if (less(b, a)) exch(a, b)
|
|
#define push(i) (stack[stackpos++] = (i))
|
|
#define pop() (stack[--stackpos])
|
|
|
|
static __forceinline
|
|
int FspFileSystemDirectoryBufferLess(PUINT8 Buffer, int a, int b)
|
|
{
|
|
FSP_FSCTL_DIR_INFO *DirInfoA = (FSP_FSCTL_DIR_INFO *)(Buffer + a);
|
|
FSP_FSCTL_DIR_INFO *DirInfoB = (FSP_FSCTL_DIR_INFO *)(Buffer + b);
|
|
return 0 > FspFileSystemDirectoryBufferFileNameCmp(
|
|
DirInfoA->FileNameBuf, (DirInfoA->Size - sizeof *DirInfoA) / sizeof(WCHAR),
|
|
DirInfoB->FileNameBuf, (DirInfoB->Size - sizeof *DirInfoB) / sizeof(WCHAR));
|
|
}
|
|
|
|
static __forceinline
|
|
int FspFileSystemPartitionDirectoryBuffer(PUINT8 Buffer, PULONG Index, int l, int r)
|
|
{
|
|
int i = l - 1, j = r;
|
|
ULONG v = Index[r];
|
|
|
|
for (;;)
|
|
{
|
|
while (less(Index[++i], v))
|
|
;
|
|
|
|
while (less(v, Index[--j]))
|
|
if (j == l)
|
|
break;
|
|
|
|
if (i >= j)
|
|
break;
|
|
|
|
exch(Index[i], Index[j]);
|
|
}
|
|
|
|
exch(Index[i], Index[r]);
|
|
|
|
return i;
|
|
}
|
|
|
|
static VOID FspFileSystemQSortDirectoryBuffer(PUINT8 Buffer, PULONG Index, int l, int r)
|
|
{
|
|
int stack[64], stackpos = 0;
|
|
int i;
|
|
|
|
for (;;)
|
|
{
|
|
while (r > l)
|
|
{
|
|
#if 1
|
|
exch(Index[(l + r) / 2], Index[r - 1]);
|
|
compexch(Index[l], Index[r - 1]);
|
|
compexch(Index[l], Index[r]);
|
|
compexch(Index[r - 1], Index[r]);
|
|
|
|
if (r - 1 <= l + 1)
|
|
break;
|
|
|
|
i = FspFileSystemPartitionDirectoryBuffer(Buffer, Index, l + 1, r - 1);
|
|
#else
|
|
i = FspFileSystemPartitionDirectoryBuffer(Buffer, Index, l, r);
|
|
#endif
|
|
|
|
if (i - l > r - i)
|
|
{
|
|
push(l); push(i - 1);
|
|
l = i + 1;
|
|
}
|
|
else
|
|
{
|
|
push(i + 1); push(r);
|
|
r = i - 1;
|
|
}
|
|
}
|
|
|
|
if (0 == stackpos)
|
|
break;
|
|
|
|
r = pop(); l = pop();
|
|
}
|
|
}
|
|
|
|
#undef push
|
|
#undef pop
|
|
#undef less
|
|
#undef compexch
|
|
#undef exch
|
|
|
|
static inline VOID FspFileSystemSortDirectoryBuffer(FSP_FILE_SYSTEM_DIRECTORY_BUFFER *DirBuffer)
|
|
{
|
|
PUINT8 Buffer = DirBuffer->Buffer;
|
|
PULONG Index = (PULONG)(DirBuffer->Buffer + DirBuffer->HiMark);
|
|
ULONG Count = (DirBuffer->Capacity - DirBuffer->HiMark) / sizeof(ULONG);
|
|
|
|
FspFileSystemQSortDirectoryBuffer(Buffer, Index, 0, Count - 1);
|
|
}
|
|
|
|
FSP_API BOOLEAN FspFileSystemAcquireDirectoryBufferEx(PVOID* PDirBuffer,
|
|
BOOLEAN Reset, ULONG CapacityHint, PNTSTATUS PResult)
|
|
{
|
|
FSP_FILE_SYSTEM_DIRECTORY_BUFFER *DirBuffer = FspInterlockedLoadPointer(PDirBuffer);
|
|
|
|
if (0 == DirBuffer)
|
|
{
|
|
static SRWLOCK CreateLock = SRWLOCK_INIT;
|
|
FSP_FILE_SYSTEM_DIRECTORY_BUFFER *NewDirBuffer;
|
|
|
|
/* compute next (or equal) power of 2; ensure fits within bounds */
|
|
ULONG bitidx;
|
|
if (_BitScanReverse(&bitidx, CapacityHint - 1))
|
|
CapacityHint = (1 << (1 + bitidx));
|
|
CapacityHint = FspFileSystemDirectoryBufferLoBound > CapacityHint ?
|
|
FspFileSystemDirectoryBufferLoBound : CapacityHint;
|
|
CapacityHint = FspFileSystemDirectoryBufferHiBound < CapacityHint ?
|
|
FspFileSystemDirectoryBufferHiBound : CapacityHint;
|
|
|
|
NewDirBuffer = MemAlloc(sizeof *NewDirBuffer);
|
|
if (0 == NewDirBuffer)
|
|
RETURN(STATUS_INSUFFICIENT_RESOURCES, FALSE);
|
|
memset(NewDirBuffer, 0, sizeof *NewDirBuffer);
|
|
NewDirBuffer->InitialCapacity = CapacityHint;
|
|
InitializeSRWLock(&NewDirBuffer->Lock);
|
|
AcquireSRWLockExclusive(&NewDirBuffer->Lock);
|
|
|
|
AcquireSRWLockExclusive(&CreateLock);
|
|
DirBuffer = FspInterlockedLoadPointer(PDirBuffer);
|
|
if (0 == DirBuffer)
|
|
FspInterlockedStorePointer(PDirBuffer, DirBuffer = NewDirBuffer);
|
|
ReleaseSRWLockExclusive(&CreateLock);
|
|
|
|
if (DirBuffer == NewDirBuffer)
|
|
RETURN(STATUS_SUCCESS, TRUE);
|
|
|
|
ReleaseSRWLockExclusive(&NewDirBuffer->Lock);
|
|
MemFree(NewDirBuffer);
|
|
}
|
|
|
|
if (Reset)
|
|
{
|
|
AcquireSRWLockExclusive(&DirBuffer->Lock);
|
|
|
|
DirBuffer->LoMark = 0;
|
|
DirBuffer->HiMark = DirBuffer->Capacity;
|
|
|
|
RETURN(STATUS_SUCCESS, TRUE);
|
|
}
|
|
|
|
RETURN(STATUS_SUCCESS, FALSE);
|
|
}
|
|
|
|
FSP_API BOOLEAN FspFileSystemAcquireDirectoryBuffer(PVOID *PDirBuffer,
|
|
BOOLEAN Reset, PNTSTATUS PResult)
|
|
{
|
|
return FspFileSystemAcquireDirectoryBufferEx(PDirBuffer, Reset, 0, PResult);
|
|
}
|
|
|
|
FSP_API BOOLEAN FspFileSystemFillDirectoryBuffer(PVOID *PDirBuffer,
|
|
FSP_FSCTL_DIR_INFO *DirInfo, PNTSTATUS PResult)
|
|
{
|
|
/* assume that FspFileSystemAcquireDirectoryBuffer has been called */
|
|
|
|
FSP_FILE_SYSTEM_DIRECTORY_BUFFER *DirBuffer = FspInterlockedLoadPointer(PDirBuffer);
|
|
ULONG Capacity, LoMark, HiMark;
|
|
PUINT8 Buffer;
|
|
|
|
if (0 == DirInfo)
|
|
RETURN(STATUS_INVALID_PARAMETER, FALSE);
|
|
|
|
for (;;)
|
|
{
|
|
LoMark = DirBuffer->LoMark;
|
|
HiMark = DirBuffer->HiMark;
|
|
Buffer = DirBuffer->Buffer;
|
|
|
|
if (FspFileSystemAddDirInfo(DirInfo,
|
|
Buffer,
|
|
HiMark > sizeof(ULONG) ? HiMark - sizeof(ULONG)/*space for new index entry*/ : HiMark,
|
|
&LoMark))
|
|
{
|
|
HiMark -= sizeof(ULONG);
|
|
*(PULONG)(Buffer + HiMark) = DirBuffer->LoMark;
|
|
|
|
DirBuffer->LoMark = LoMark;
|
|
DirBuffer->HiMark = HiMark;
|
|
|
|
RETURN (STATUS_SUCCESS, TRUE);
|
|
}
|
|
|
|
if (0 == Buffer)
|
|
{
|
|
Buffer = MemAlloc(Capacity = DirBuffer->InitialCapacity);
|
|
if (0 == Buffer)
|
|
RETURN(STATUS_INSUFFICIENT_RESOURCES, FALSE);
|
|
|
|
HiMark = Capacity;
|
|
}
|
|
else
|
|
{
|
|
ULONG Factor = FspFileSystemDirectoryBufferHiBound > DirBuffer->Capacity ?
|
|
FspFileSystemDirectoryBufferLoFactor : FspFileSystemDirectoryBufferHiFactor;
|
|
Buffer = MemRealloc(Buffer, Capacity = DirBuffer->Capacity * Factor);
|
|
if (0 == Buffer)
|
|
RETURN(STATUS_INSUFFICIENT_RESOURCES, FALSE);
|
|
|
|
ULONG IndexSize = DirBuffer->Capacity - HiMark;
|
|
ULONG NewHiMark = Capacity - IndexSize;
|
|
|
|
memmove(Buffer + NewHiMark, Buffer + HiMark, IndexSize);
|
|
HiMark = NewHiMark;
|
|
}
|
|
|
|
DirBuffer->Capacity = Capacity;
|
|
DirBuffer->LoMark = LoMark;
|
|
DirBuffer->HiMark = HiMark;
|
|
DirBuffer->Buffer = Buffer;
|
|
}
|
|
}
|
|
|
|
FSP_API VOID FspFileSystemReleaseDirectoryBuffer(PVOID *PDirBuffer)
|
|
{
|
|
/* assume that FspFileSystemAcquireDirectoryBuffer has been called */
|
|
|
|
FSP_FILE_SYSTEM_DIRECTORY_BUFFER *DirBuffer = FspInterlockedLoadPointer(PDirBuffer);
|
|
|
|
/* eliminate invalidated entries from the index */
|
|
PULONG Index = (PULONG)(DirBuffer->Buffer + DirBuffer->HiMark);
|
|
ULONG Count = (DirBuffer->Capacity - DirBuffer->HiMark) / sizeof(ULONG);
|
|
ULONG I, J;
|
|
for (I = Count - 1, J = Count; I < Count; I--)
|
|
{
|
|
if (FspFileSystemDirectoryBufferEntryInvalid == Index[I])
|
|
continue;
|
|
Index[--J] = Index[I];
|
|
}
|
|
DirBuffer->HiMark = (ULONG)((PUINT8)&Index[J] - DirBuffer->Buffer);
|
|
|
|
FspFileSystemSortDirectoryBuffer(DirBuffer);
|
|
|
|
ReleaseSRWLockExclusive(&DirBuffer->Lock);
|
|
}
|
|
|
|
FSP_API VOID FspFileSystemReadDirectoryBuffer(PVOID *PDirBuffer,
|
|
PWSTR Marker,
|
|
PVOID Buffer, ULONG Length, PULONG PBytesTransferred)
|
|
{
|
|
FSP_FILE_SYSTEM_DIRECTORY_BUFFER *DirBuffer = FspInterlockedLoadPointer(PDirBuffer);
|
|
|
|
if (0 != DirBuffer)
|
|
{
|
|
AcquireSRWLockShared(&DirBuffer->Lock);
|
|
|
|
PULONG Index = (PULONG)(DirBuffer->Buffer + DirBuffer->HiMark);
|
|
ULONG Count = (DirBuffer->Capacity - DirBuffer->HiMark) / sizeof(ULONG);
|
|
ULONG IndexNum;
|
|
FSP_FSCTL_DIR_INFO *DirInfo;
|
|
|
|
if (0 == Marker)
|
|
IndexNum = 0;
|
|
else
|
|
{
|
|
FspFileSystemSearchDirectoryBuffer(DirBuffer,
|
|
Marker, lstrlenW(Marker),
|
|
&IndexNum);
|
|
IndexNum++;
|
|
}
|
|
|
|
for (; IndexNum < Count; IndexNum++)
|
|
{
|
|
DirInfo = (PVOID)(DirBuffer->Buffer + Index[IndexNum]);
|
|
if (!FspFileSystemAddDirInfo(DirInfo, Buffer, Length, PBytesTransferred))
|
|
{
|
|
ReleaseSRWLockShared(&DirBuffer->Lock);
|
|
return;
|
|
}
|
|
}
|
|
|
|
ReleaseSRWLockShared(&DirBuffer->Lock);
|
|
}
|
|
|
|
FspFileSystemAddDirInfo(0, Buffer, Length, PBytesTransferred);
|
|
}
|
|
|
|
FSP_API VOID FspFileSystemDeleteDirectoryBuffer(PVOID *PDirBuffer)
|
|
{
|
|
FSP_FILE_SYSTEM_DIRECTORY_BUFFER *DirBuffer = FspInterlockedLoadPointer(PDirBuffer);
|
|
|
|
if (0 != DirBuffer)
|
|
{
|
|
MemFree(DirBuffer->Buffer);
|
|
MemFree(DirBuffer);
|
|
FspInterlockedStorePointer(PDirBuffer, 0);
|
|
}
|
|
}
|
|
|
|
VOID FspFileSystemPeekInDirectoryBuffer(PVOID *PDirBuffer,
|
|
PUINT8 *PBuffer, PULONG *PIndex, PULONG PCount)
|
|
{
|
|
/* assume that FspFileSystemAcquireDirectoryBuffer has been called */
|
|
|
|
FSP_FILE_SYSTEM_DIRECTORY_BUFFER *DirBuffer = FspInterlockedLoadPointer(PDirBuffer);
|
|
|
|
*PBuffer = DirBuffer->Buffer;
|
|
*PIndex = (PULONG)(DirBuffer->Buffer + DirBuffer->HiMark);
|
|
*PCount = (DirBuffer->Capacity - DirBuffer->HiMark) / sizeof(ULONG);
|
|
}
|