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);