winfsp/tst/winfsp-tests/dirbuf-test.c
2024-01-08 12:38:44 +00:00

515 lines
16 KiB
C

/**
* @file dirbuf-test.c
*
* @copyright 2015-2024 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 <winfsp/winfsp.h>
#include <tlib/testsuite.h>
#include <strsafe.h>
#include <time.h>
#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, ULONG InvalidCount, BOOLEAN Presort)
{
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;
UINT64 PresortIndex = 0;
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 if (!Presort)
{
N = 24 + rand() % (32 - 24);
for (ULONG J = 0; N > J; J++)
DirInfo->FileNameBuf[J] = 'A' + rand() % 26;
}
else
{
N = 24;
StringCbPrintfW(DirInfo->FileNameBuf, 64, L"FILEFILE%016llx", PresortIndex);
PresortIndex++;
}
DirInfo->Size = (UINT16)(sizeof(FSP_FSCTL_DIR_INFO) + N * sizeof(WCHAR));
Success = FspFileSystemFillDirectoryBuffer(&DirBuffer, DirInfo, &Result);
ASSERT(Success);
ASSERT(STATUS_SUCCESS == Result);
}
#if 1
{
ASSERT(Count > InvalidCount);
#if !defined(FspFileSystemDirectoryBufferEntryInvalid)
const ULONG FspFileSystemDirectoryBufferEntryInvalid = ((ULONG)-1);
#endif
typedef struct
{
SRWLOCK Lock;
ULONG InitialCapacity, Capacity, LoMark, HiMark;
PUINT8 Buffer;
} FSP_FILE_SYSTEM_DIRECTORY_BUFFER;
FSP_FILE_SYSTEM_DIRECTORY_BUFFER *PeekDirBuffer = DirBuffer;
PUINT8 PeekBuffer = PeekDirBuffer->Buffer;
PULONG PeekIndex = (PULONG)(PeekDirBuffer->Buffer + PeekDirBuffer->HiMark);
ULONG PeekCount = (PeekDirBuffer->Capacity - PeekDirBuffer->HiMark) / sizeof(ULONG);
ASSERT(Count == PeekCount);
for (ULONG I = 0; InvalidCount > I; I++)
{
for (;;)
{
N = rand() % PeekCount;
if (FspFileSystemDirectoryBufferEntryInvalid == PeekIndex[N])
continue;
DirInfo = (PVOID)(PeekBuffer + PeekIndex[N]);
memcpy(CurrFileName, DirInfo->FileNameBuf, DirInfo->Size - sizeof *DirInfo);
CurrFileName[(DirInfo->Size - sizeof *DirInfo) / sizeof(WCHAR)] = L'\0';
/* do not invalidate dot entries as they are used in testing below */
if (0 == wcscmp(CurrFileName, L".") || 0 == wcscmp(CurrFileName, L".."))
continue;
PeekIndex[N] = FspFileSystemDirectoryBufferEntryInvalid;
break;
}
}
Count -= InvalidCount;
}
#endif
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, 0, FALSE);
dirbuf_fill_dotest(1485473509, 10, 3, FALSE);
for (ULONG I = 0; 10000 > I; I++)
{
dirbuf_fill_dotest(seed + I, 10, 0, FALSE);
dirbuf_fill_dotest(seed + I, 10, 3, FALSE);
}
for (ULONG I = 0; 1000 > I; I++)
{
dirbuf_fill_dotest(seed + I, 100, 0, FALSE);
dirbuf_fill_dotest(seed + I, 100, 30, FALSE);
}
for (ULONG I = 0; 100 > I; I++)
{
dirbuf_fill_dotest(seed + I, 1000, 0, FALSE);
dirbuf_fill_dotest(seed + I, 1000, 300, FALSE);
}
for (ULONG I = 0; 10 > I; I++)
{
dirbuf_fill_dotest(seed + I, 10000, 0, FALSE);
dirbuf_fill_dotest(seed + I, 10000, 3000, FALSE);
}
}
static void dirbuf_presort_fill_test(void)
{
unsigned seed = (unsigned)time(0);
for (ULONG I = 0; 10000 > I; I++)
{
dirbuf_fill_dotest(seed + I, 10, 0, TRUE);
dirbuf_fill_dotest(seed + I, 10, 3, TRUE);
}
for (ULONG I = 0; 1000 > I; I++)
{
dirbuf_fill_dotest(seed + I, 100, 0, TRUE);
dirbuf_fill_dotest(seed + I, 100, 30, TRUE);
}
for (ULONG I = 0; 100 > I; I++)
{
dirbuf_fill_dotest(seed + I, 1000, 0, TRUE);
dirbuf_fill_dotest(seed + I, 1000, 300, TRUE);
}
for (ULONG I = 0; 10 > I; I++)
{
dirbuf_fill_dotest(seed + I, 10000, 0, FALSE);
dirbuf_fill_dotest(seed + I, 10000, 3000, FALSE);
}
}
static void dirbuf_boundary_dotest(PWSTR Marker, ULONG ExpectI, ULONG ExpectN, ...)
{
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;
PWSTR Name;
va_list Names0, Names1;
va_start(Names0, ExpectN);
va_copy(Names1, Names0);
Length = sizeof Buffer;
Result = STATUS_UNSUCCESSFUL;
Success = FspFileSystemAcquireDirectoryBuffer(&DirBuffer, FALSE, &Result);
ASSERT(Success);
ASSERT(STATUS_SUCCESS == Result);
while (0 != (Name = va_arg(Names0, PWSTR)))
{
memset(&DirInfoBuf, 0, sizeof DirInfoBuf);
DirInfo->Size = (UINT16)(sizeof(FSP_FSCTL_DIR_INFO) + wcslen(Name) * sizeof(WCHAR));
wcscpy_s(DirInfo->FileNameBuf, MAX_PATH, Name);
Success = FspFileSystemFillDirectoryBuffer(&DirBuffer, DirInfo, &Result);
ASSERT(Success);
ASSERT(STATUS_SUCCESS == Result);
}
FspFileSystemReleaseDirectoryBuffer(&DirBuffer);
BytesTransferred = 0;
FspFileSystemReadDirectoryBuffer(&DirBuffer, Marker, Buffer, Length, &BytesTransferred);
for (N = 0; ExpectI > N; N++)
va_arg(Names1, PWSTR);
for (N = 0,
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';
Name = va_arg(Names1, PWSTR);
ASSERT(0 == wcscmp(CurrFileName, Name));
}
ASSERT(ExpectN == N);
FspFileSystemDeleteDirectoryBuffer(&DirBuffer);
va_end(Names1);
va_end(Names0);
}
static void dirbuf_boundary_test(void)
{
dirbuf_boundary_dotest(L"A", 0, 0, 0);
dirbuf_boundary_dotest(L"A", 0, 1, L"B", 0);
dirbuf_boundary_dotest(L"B", 0, 0, L"B", 0);
dirbuf_boundary_dotest(L"C", 0, 0, L"B", 0);
dirbuf_boundary_dotest(L"A", 0, 2, L"B", L"D", 0);
dirbuf_boundary_dotest(L"B", 1, 1, L"B", L"D", 0);
dirbuf_boundary_dotest(L"C", 1, 1, L"B", L"D", 0);
dirbuf_boundary_dotest(L"D", 0, 0, L"B", L"D", 0);
dirbuf_boundary_dotest(L"E", 0, 0, L"B", L"D", 0);
dirbuf_boundary_dotest(L"A", 0, 3, L"B", L"D", L"F", 0);
dirbuf_boundary_dotest(L"B", 1, 2, L"B", L"D", L"F", 0);
dirbuf_boundary_dotest(L"C", 1, 2, L"B", L"D", L"F", 0);
dirbuf_boundary_dotest(L"D", 2, 1, L"B", L"D", L"F", 0);
dirbuf_boundary_dotest(L"E", 2, 1, L"B", L"D", L"F", 0);
dirbuf_boundary_dotest(L"F", 0, 0, L"B", L"D", L"F", 0);
dirbuf_boundary_dotest(L"G", 0, 0, L"B", L"D", L"F", 0);
}
void dirbuf_tests(void)
{
if (OptExternal)
return;
TEST(dirbuf_empty_test);
TEST(dirbuf_dots_test);
TEST(dirbuf_fill_test);
TEST(dirbuf_presort_fill_test);
TEST(dirbuf_boundary_test);
}