diff --git a/build/VStudio/testing/winfsp-tests.vcxproj b/build/VStudio/testing/winfsp-tests.vcxproj
index 70850c17..c15e3f4a 100644
--- a/build/VStudio/testing/winfsp-tests.vcxproj
+++ b/build/VStudio/testing/winfsp-tests.vcxproj
@@ -182,6 +182,7 @@
+
diff --git a/build/VStudio/testing/winfsp-tests.vcxproj.filters b/build/VStudio/testing/winfsp-tests.vcxproj.filters
index 5d5ba4c6..a2afe2f8 100644
--- a/build/VStudio/testing/winfsp-tests.vcxproj.filters
+++ b/build/VStudio/testing/winfsp-tests.vcxproj.filters
@@ -76,6 +76,9 @@
Source
+
+ Source
+
diff --git a/build/VStudio/winfsp_dll.vcxproj b/build/VStudio/winfsp_dll.vcxproj
index fb9e3fe7..91cc4568 100644
--- a/build/VStudio/winfsp_dll.vcxproj
+++ b/build/VStudio/winfsp_dll.vcxproj
@@ -31,6 +31,7 @@
+
diff --git a/build/VStudio/winfsp_dll.vcxproj.filters b/build/VStudio/winfsp_dll.vcxproj.filters
index ff6b31b3..514c222f 100644
--- a/build/VStudio/winfsp_dll.vcxproj.filters
+++ b/build/VStudio/winfsp_dll.vcxproj.filters
@@ -103,6 +103,9 @@
Source\fuse
+
+ Source
+
diff --git a/inc/winfsp/winfsp.h b/inc/winfsp/winfsp.h
index e28cf0b1..4300d9ba 100644
--- a/inc/winfsp/winfsp.h
+++ b/inc/winfsp/winfsp.h
@@ -1293,6 +1293,19 @@ FSP_API NTSTATUS FspFileSystemCanReplaceReparsePoint(
FSP_API BOOLEAN FspFileSystemAddStreamInfo(FSP_FSCTL_STREAM_INFO *StreamInfo,
PVOID Buffer, ULONG Length, PULONG PBytesTransferred);
+/*
+ * Directory buffering
+ */
+FSP_API BOOLEAN FspFileSystemAcquireDirectoryBuffer(PVOID *PDirBuffer,
+ BOOLEAN Reset, PNTSTATUS PResult);
+FSP_API BOOLEAN FspFileSystemFillDirectoryBuffer(PVOID *PDirBuffer,
+ FSP_FSCTL_DIR_INFO *DirInfo, PNTSTATUS PResult);
+FSP_API VOID FspFileSystemReleaseDirectoryBuffer(PVOID *PDirBuffer);
+FSP_API VOID FspFileSystemReadDirectoryBuffer(PVOID *PDirBuffer,
+ PWSTR Marker,
+ PVOID Buffer, ULONG Length, PULONG PBytesTransferred);
+FSP_API VOID FspFileSystemDeleteDirectoryBuffer(PVOID *PDirBuffer);
+
/*
* Security
*/
diff --git a/src/dll/dirbuf.c b/src/dll/dirbuf.c
new file mode 100644
index 00000000..4227a71d
--- /dev/null
+++ b/src/dll/dirbuf.c
@@ -0,0 +1,392 @@
+/**
+ * @file dll/dirbuf.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 RETURN(R, B) \
+ do \
+ { \
+ if (0 != PResult) \
+ *PResult = R; \
+ return B; \
+ } while (0,0)
+
+typedef struct
+{
+ SRWLOCK Lock;
+ ULONG 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 = Lo;
+ 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 0
+ 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]);
+
+ 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 FspFileSystemAcquireDirectoryBuffer(PVOID *PDirBuffer,
+ BOOLEAN Reset, PNTSTATUS PResult)
+{
+ FSP_FILE_SYSTEM_DIRECTORY_BUFFER *DirBuffer = *PDirBuffer;
+ MemoryBarrier();
+
+ if (0 == DirBuffer)
+ {
+ static SRWLOCK CreateLock = SRWLOCK_INIT;
+ FSP_FILE_SYSTEM_DIRECTORY_BUFFER *NewDirBuffer;
+
+ NewDirBuffer = MemAlloc(sizeof *NewDirBuffer);
+ if (0 == NewDirBuffer)
+ RETURN(STATUS_INSUFFICIENT_RESOURCES, FALSE);
+ memset(NewDirBuffer, 0, sizeof *NewDirBuffer);
+ InitializeSRWLock(&NewDirBuffer->Lock);
+ AcquireSRWLockExclusive(&NewDirBuffer->Lock);
+
+ AcquireSRWLockExclusive(&CreateLock);
+ DirBuffer = *PDirBuffer;
+ MemoryBarrier();
+ if (0 == DirBuffer)
+ *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 FspFileSystemFillDirectoryBuffer(PVOID *PDirBuffer,
+ FSP_FSCTL_DIR_INFO *DirInfo, PNTSTATUS PResult)
+{
+ /* assume that FspFileSystemAcquireDirectoryBuffer has been called */
+
+ FSP_FILE_SYSTEM_DIRECTORY_BUFFER *DirBuffer = *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 = 512);
+ if (0 == Buffer)
+ RETURN(STATUS_INSUFFICIENT_RESOURCES, FALSE);
+
+ HiMark = Capacity;
+ }
+ else
+ {
+ Buffer = MemRealloc(Buffer, Capacity = DirBuffer->Capacity * 2);
+ 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 = *PDirBuffer;
+
+ 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 = *PDirBuffer;
+ MemoryBarrier();
+
+ 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 = *PDirBuffer;
+ MemoryBarrier();
+
+ if (0 != DirBuffer)
+ {
+ MemFree(DirBuffer->Buffer);
+ MemFree(DirBuffer);
+ *PDirBuffer = 0;
+ }
+}
diff --git a/src/shared/minimal.h b/src/shared/minimal.h
index a3801398..9b7f5cae 100644
--- a/src/shared/minimal.h
+++ b/src/shared/minimal.h
@@ -126,6 +126,10 @@ static inline void *MemAlloc(size_t Size)
{
return HeapAlloc(GetProcessHeap(), 0, Size);
}
+static inline void *MemRealloc(void *Pointer, size_t Size)
+{
+ return HeapReAlloc(GetProcessHeap(), 0, Pointer, Size);
+}
static inline void MemFree(void *Pointer)
{
if (0 != Pointer)
diff --git a/tst/winfsp-tests/dirbuf-test.c b/tst/winfsp-tests/dirbuf-test.c
new file mode 100644
index 00000000..bb60fa84
--- /dev/null
+++ b/tst/winfsp-tests/dirbuf-test.c
@@ -0,0 +1,322 @@
+/**
+ * @file dirbuf-test.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
+#include
+#include
+
+#include "winfsp-tests.h"
+
+static void dirbuf_empty_test(void)
+{
+ PVOID DirBuffer = 0;
+ NTSTATUS Result;
+ BOOLEAN Success;
+ UINT8 Buffer[64];
+ ULONG BytesTransferred;
+
+ BytesTransferred = 0;
+ FspFileSystemReadDirectoryBuffer(&DirBuffer, 0, Buffer, sizeof Buffer, &BytesTransferred);
+ ASSERT(sizeof(UINT16) == BytesTransferred);
+
+ Result = STATUS_UNSUCCESSFUL;
+ Success = FspFileSystemAcquireDirectoryBuffer(&DirBuffer, FALSE, &Result);
+ ASSERT(Success);
+ ASSERT(STATUS_SUCCESS == Result);
+
+ FspFileSystemReleaseDirectoryBuffer(&DirBuffer);
+
+ BytesTransferred = 0;
+ FspFileSystemReadDirectoryBuffer(&DirBuffer, 0, Buffer, sizeof Buffer, &BytesTransferred);
+ ASSERT(sizeof(UINT16) == BytesTransferred);
+
+ Result = STATUS_UNSUCCESSFUL;
+ Success = FspFileSystemAcquireDirectoryBuffer(&DirBuffer, FALSE, &Result);
+ ASSERT(!Success);
+ ASSERT(STATUS_SUCCESS == Result);
+
+ BytesTransferred = 0;
+ FspFileSystemReadDirectoryBuffer(&DirBuffer, 0, Buffer, sizeof Buffer, &BytesTransferred);
+ ASSERT(sizeof(UINT16) == BytesTransferred);
+
+ Result = STATUS_UNSUCCESSFUL;
+ Success = FspFileSystemAcquireDirectoryBuffer(&DirBuffer, TRUE, &Result);
+ ASSERT(Success);
+ ASSERT(STATUS_SUCCESS == Result);
+
+ FspFileSystemReleaseDirectoryBuffer(&DirBuffer);
+
+ BytesTransferred = 0;
+ FspFileSystemReadDirectoryBuffer(&DirBuffer, 0, Buffer, sizeof Buffer, &BytesTransferred);
+ ASSERT(sizeof(UINT16) == BytesTransferred);
+
+ FspFileSystemDeleteDirectoryBuffer(&DirBuffer);
+ FspFileSystemDeleteDirectoryBuffer(&DirBuffer);
+}
+
+static void dirbuf_dots_test(void)
+{
+ PVOID DirBuffer = 0;
+ NTSTATUS Result;
+ BOOLEAN Success;
+ union
+ {
+ UINT8 B[sizeof(FSP_FSCTL_DIR_INFO) + MAX_PATH * sizeof(WCHAR)];
+ FSP_FSCTL_DIR_INFO D;
+ } DirInfoBuf;
+ FSP_FSCTL_DIR_INFO *DirInfo = &DirInfoBuf.D, *DirInfoEnd;
+ UINT8 Buffer[1024];
+ ULONG Length, BytesTransferred;
+ WCHAR CurrFileName[MAX_PATH];
+ ULONG N;
+
+ Length = sizeof Buffer;
+
+ Result = STATUS_UNSUCCESSFUL;
+ Success = FspFileSystemAcquireDirectoryBuffer(&DirBuffer, FALSE, &Result);
+ ASSERT(Success);
+ ASSERT(STATUS_SUCCESS == Result);
+
+ memset(&DirInfoBuf, 0, sizeof DirInfoBuf);
+ DirInfo->Size = (UINT16)(sizeof(FSP_FSCTL_DIR_INFO) + 1 * sizeof(WCHAR));
+ DirInfo->FileNameBuf[0] = L'.';
+ Success = FspFileSystemFillDirectoryBuffer(&DirBuffer, DirInfo, &Result);
+ ASSERT(Success);
+ ASSERT(STATUS_SUCCESS == Result);
+
+ memset(&DirInfoBuf, 0, sizeof DirInfoBuf);
+ DirInfo->Size = (UINT16)(sizeof(FSP_FSCTL_DIR_INFO) + 1 * sizeof(WCHAR));
+ DirInfo->FileNameBuf[0] = L' ';
+ Success = FspFileSystemFillDirectoryBuffer(&DirBuffer, DirInfo, &Result);
+ ASSERT(Success);
+ ASSERT(STATUS_SUCCESS == Result);
+
+ memset(&DirInfoBuf, 0, sizeof DirInfoBuf);
+ DirInfo->Size = (UINT16)(sizeof(FSP_FSCTL_DIR_INFO) + 2 * sizeof(WCHAR));
+ DirInfo->FileNameBuf[0] = L'.';
+ DirInfo->FileNameBuf[1] = L'.';
+ Success = FspFileSystemFillDirectoryBuffer(&DirBuffer, DirInfo, &Result);
+ ASSERT(Success);
+ ASSERT(STATUS_SUCCESS == Result);
+
+ FspFileSystemReleaseDirectoryBuffer(&DirBuffer);
+
+ BytesTransferred = 0;
+ FspFileSystemReadDirectoryBuffer(&DirBuffer, 0, Buffer, Length, &BytesTransferred);
+
+ N = 0;
+ for (
+ DirInfo = (PVOID)Buffer, DirInfoEnd = (PVOID)(Buffer + BytesTransferred);
+ DirInfoEnd > DirInfo && 0 != DirInfo->Size;
+ DirInfo = (PVOID)((PUINT8)DirInfo + FSP_FSCTL_DEFAULT_ALIGN_UP(DirInfo->Size)), N++)
+ {
+ memcpy(CurrFileName, DirInfo->FileNameBuf, DirInfo->Size - sizeof *DirInfo);
+ CurrFileName[(DirInfo->Size - sizeof *DirInfo) / sizeof(WCHAR)] = L'\0';
+
+ if (0 == N)
+ ASSERT(0 == wcscmp(CurrFileName, L"."));
+ else if (1 == N)
+ ASSERT(0 == wcscmp(CurrFileName, L".."));
+ else if (2 == N)
+ ASSERT(0 == wcscmp(CurrFileName, L" "));
+ }
+ ASSERT(DirInfoEnd > DirInfo);
+ ASSERT(0 == DirInfo->Size);
+ ASSERT(N == 3);
+
+ FspFileSystemDeleteDirectoryBuffer(&DirBuffer);
+}
+
+static void dirbuf_fill_dotest(unsigned seed, ULONG Count)
+{
+ PVOID DirBuffer = 0;
+ NTSTATUS Result;
+ BOOLEAN Success;
+ union
+ {
+ UINT8 B[sizeof(FSP_FSCTL_DIR_INFO) + MAX_PATH * sizeof(WCHAR)];
+ FSP_FSCTL_DIR_INFO D;
+ } DirInfoBuf;
+ FSP_FSCTL_DIR_INFO *DirInfo = &DirInfoBuf.D, *DirInfoEnd;
+ PUINT8 Buffer;
+ ULONG Length, BytesTransferred;
+ WCHAR CurrFileName[MAX_PATH], PrevFileName[MAX_PATH];
+ ULONG DotIndex = Count / 3, DotDotIndex = Count / 3 * 2;
+ WCHAR Marker[MAX_PATH];
+ ULONG N, MarkerIndex, MarkerLength;
+
+ srand(seed);
+
+ Length = Count * FSP_FSCTL_DEFAULT_ALIGN_UP(sizeof DirInfoBuf) + sizeof(UINT16);
+ Buffer = malloc(Length);
+ ASSERT(0 != Buffer);
+
+ Result = STATUS_UNSUCCESSFUL;
+ Success = FspFileSystemAcquireDirectoryBuffer(&DirBuffer, FALSE, &Result);
+ ASSERT(Success);
+ ASSERT(STATUS_SUCCESS == Result);
+
+ for (ULONG I = 0; Count > I; I++)
+ {
+ memset(&DirInfoBuf, 0, sizeof DirInfoBuf);
+
+ if (I == DotIndex)
+ {
+ N = 1;
+ DirInfo->FileNameBuf[0] = L'.';
+ }
+ else if (I == DotDotIndex)
+ {
+ N = 2;
+ DirInfo->FileNameBuf[0] = L'.';
+ DirInfo->FileNameBuf[1] = L'.';
+ }
+ else
+ {
+ N = 24 + rand() % (32 - 24);
+ for (ULONG J = 0; N > J; J++)
+ DirInfo->FileNameBuf[J] = 'A' + rand() % 26;
+ }
+ DirInfo->Size = (UINT16)(sizeof(FSP_FSCTL_DIR_INFO) + N * sizeof(WCHAR));
+
+ Success = FspFileSystemFillDirectoryBuffer(&DirBuffer, DirInfo, &Result);
+ ASSERT(Success);
+ ASSERT(STATUS_SUCCESS == Result);
+ }
+
+ FspFileSystemReleaseDirectoryBuffer(&DirBuffer);
+
+ MarkerIndex = rand() % Count;
+
+ BytesTransferred = 0;
+ FspFileSystemReadDirectoryBuffer(&DirBuffer, 0, Buffer, Length, &BytesTransferred);
+
+ N = 0;
+ PrevFileName[0] = L'\0';
+ for (
+ DirInfo = (PVOID)Buffer, DirInfoEnd = (PVOID)(Buffer + BytesTransferred);
+ DirInfoEnd > DirInfo && 0 != DirInfo->Size;
+ DirInfo = (PVOID)((PUINT8)DirInfo + FSP_FSCTL_DEFAULT_ALIGN_UP(DirInfo->Size)), N++)
+ {
+ memcpy(CurrFileName, DirInfo->FileNameBuf, DirInfo->Size - sizeof *DirInfo);
+ CurrFileName[(DirInfo->Size - sizeof *DirInfo) / sizeof(WCHAR)] = L'\0';
+
+ if (0 == N)
+ ASSERT(0 == wcscmp(CurrFileName, L"."));
+ else if (1 == N)
+ ASSERT(0 == wcscmp(CurrFileName, L".."));
+ else
+ ASSERT(0 != wcscmp(CurrFileName, L".") && 0 != wcscmp(CurrFileName, L".."));
+
+ //ASSERT(wcscmp(PrevFileName, CurrFileName) <= 0);
+ /* filenames are random enought that should not happen in practice */
+ ASSERT(wcscmp(PrevFileName, CurrFileName) < 0);
+
+ memcpy(PrevFileName, CurrFileName, sizeof CurrFileName);
+
+ if (N == MarkerIndex)
+ {
+ memcpy(Marker, CurrFileName, sizeof CurrFileName);
+ MarkerLength = (ULONG)((PUINT8)DirInfo + FSP_FSCTL_DEFAULT_ALIGN_UP(DirInfo->Size) - Buffer);
+ }
+ }
+ ASSERT(DirInfoEnd > DirInfo);
+ ASSERT(0 == DirInfo->Size);
+ ASSERT(N == Count);
+
+ BytesTransferred = 0;
+ FspFileSystemReadDirectoryBuffer(&DirBuffer, Marker, Buffer, Length, &BytesTransferred);
+
+ N = 0;
+ PrevFileName[0] = L'\0';
+ for (
+ DirInfo = (PVOID)Buffer, DirInfoEnd = (PVOID)(Buffer + BytesTransferred);
+ DirInfoEnd > DirInfo && 0 != DirInfo->Size;
+ DirInfo = (PVOID)((PUINT8)DirInfo + FSP_FSCTL_DEFAULT_ALIGN_UP(DirInfo->Size)), N++)
+ {
+ memcpy(CurrFileName, DirInfo->FileNameBuf, DirInfo->Size - sizeof *DirInfo);
+ CurrFileName[(DirInfo->Size - sizeof *DirInfo) / sizeof(WCHAR)] = L'\0';
+
+ ASSERT(0 != wcscmp(CurrFileName, Marker));
+
+ //ASSERT(wcscmp(PrevFileName, CurrFileName) <= 0);
+ /* filenames are random enought that should not happen in practice */
+ ASSERT(wcscmp(PrevFileName, CurrFileName) < 0);
+
+ memcpy(PrevFileName, CurrFileName, sizeof CurrFileName);
+ }
+ ASSERT(DirInfoEnd > DirInfo);
+ ASSERT(0 == DirInfo->Size);
+ ASSERT(N == Count - MarkerIndex - 1);
+
+ BytesTransferred = 0;
+ FspFileSystemReadDirectoryBuffer(&DirBuffer, 0, Buffer, MarkerLength, &BytesTransferred);
+
+ N = 0;
+ PrevFileName[0] = L'\0';
+ for (
+ DirInfo = (PVOID)Buffer, DirInfoEnd = (PVOID)(Buffer + BytesTransferred);
+ DirInfoEnd > DirInfo && 0 != DirInfo->Size;
+ DirInfo = (PVOID)((PUINT8)DirInfo + FSP_FSCTL_DEFAULT_ALIGN_UP(DirInfo->Size)), N++)
+ {
+ memcpy(CurrFileName, DirInfo->FileNameBuf, DirInfo->Size - sizeof *DirInfo);
+ CurrFileName[(DirInfo->Size - sizeof *DirInfo) / sizeof(WCHAR)] = L'\0';
+
+ if (N == MarkerIndex)
+ ASSERT(0 == wcscmp(CurrFileName, Marker));
+ else
+ ASSERT(0 != wcscmp(CurrFileName, Marker));
+
+ //ASSERT(wcscmp(PrevFileName, CurrFileName) <= 0);
+ /* filenames are random enought that should not happen in practice */
+ ASSERT(wcscmp(PrevFileName, CurrFileName) < 0);
+
+ memcpy(PrevFileName, CurrFileName, sizeof CurrFileName);
+ }
+ ASSERT(DirInfoEnd <= DirInfo);
+ ASSERT(N == MarkerIndex + 1);
+
+ FspFileSystemDeleteDirectoryBuffer(&DirBuffer);
+
+ free(Buffer);
+}
+
+static void dirbuf_fill_test(void)
+{
+ unsigned seed = (unsigned)time(0);
+
+ dirbuf_fill_dotest(1485473509, 10);
+
+ for (ULONG I = 0; 10000 > I; I++)
+ dirbuf_fill_dotest(seed + I, 10);
+
+ for (ULONG I = 0; 1000 > I; I++)
+ dirbuf_fill_dotest(seed + I, 100);
+
+ for (ULONG I = 0; 100 > I; I++)
+ dirbuf_fill_dotest(seed + I, 1000);
+
+ for (ULONG I = 0; 10 > I; I++)
+ dirbuf_fill_dotest(seed + I, 10000);
+}
+
+void dirbuf_tests(void)
+{
+ TEST(dirbuf_empty_test);
+ TEST(dirbuf_dots_test);
+ TEST(dirbuf_fill_test);
+}
diff --git a/tst/winfsp-tests/winfsp-tests.c b/tst/winfsp-tests/winfsp-tests.c
index 6760668f..5f0211f5 100644
--- a/tst/winfsp-tests/winfsp-tests.c
+++ b/tst/winfsp-tests/winfsp-tests.c
@@ -182,6 +182,7 @@ int main(int argc, char *argv[])
TESTSUITE(posix_tests);
TESTSUITE(eventlog_tests);
TESTSUITE(path_tests);
+ TESTSUITE(dirbuf_tests);
TESTSUITE(mount_tests);
TESTSUITE(timeout_tests);
TESTSUITE(memfs_tests);