From b4c39f656cd12599c0fd3ef7ef68b50ec79ace80 Mon Sep 17 00:00:00 2001 From: Bill Zissimopoulos Date: Mon, 13 Apr 2020 15:52:03 -0700 Subject: [PATCH] dll,fuse: allow dir buffer entry invalidation The FUSE implementation of ReadDirectory issues readdir followed by a slew of getattr. In the current implementation if a getattr fails the whole readdir operation fails. This commit adds the ability to invalidate individual entries in the directory buffer. Entries for which getattr fails are now marked invalid rather than fail the overall ReadDirectory operation. See #292 --- src/dll/dirbuf.c | 12 ++++++ src/dll/fuse/fuse_intf.c | 3 +- src/dll/library.h | 1 + tst/winfsp-tests/dirbuf-test.c | 70 +++++++++++++++++++++++++++++++--- 4 files changed, 79 insertions(+), 7 deletions(-) diff --git a/src/dll/dirbuf.c b/src/dll/dirbuf.c index cca13d67..d2605578 100644 --- a/src/dll/dirbuf.c +++ b/src/dll/dirbuf.c @@ -335,6 +335,18 @@ FSP_API VOID FspFileSystemReleaseDirectoryBuffer(PVOID *PDirBuffer) FSP_FILE_SYSTEM_DIRECTORY_BUFFER *DirBuffer = *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); diff --git a/src/dll/fuse/fuse_intf.c b/src/dll/fuse/fuse_intf.c index ae79d737..bc0fea5a 100644 --- a/src/dll/fuse/fuse_intf.c +++ b/src/dll/fuse/fuse_intf.c @@ -1866,7 +1866,8 @@ static NTSTATUS fsp_fuse_intf_FixDirInfo(FSP_FILE_SYSTEM *FileSystem, Result = fsp_fuse_intf_GetFileInfoEx(FileSystem, PosixPath, 0, &Uid, &Gid, &Mode, &DirInfo->FileInfo); if (!NT_SUCCESS(Result)) - goto exit; + /* mark the directory buffer entry as invalid */ + *Index = FspFileSystemDirectoryBufferEntryInvalid; if (0 != PosixPathEnd) *PosixPathEnd = SavedPathChar; diff --git a/src/dll/library.h b/src/dll/library.h index d8582f8e..b90c8528 100644 --- a/src/dll/library.h +++ b/src/dll/library.h @@ -66,6 +66,7 @@ PSID FspWksidGet(WELL_KNOWN_SID_TYPE WellKnownSidType); PWSTR FspDiagIdent(VOID); +#define FspFileSystemDirectoryBufferEntryInvalid ((ULONG)-1) VOID FspFileSystemPeekInDirectoryBuffer(PVOID *PDirBuffer, PUINT8 *PBuffer, PULONG *PIndex, PULONG PCount); diff --git a/tst/winfsp-tests/dirbuf-test.c b/tst/winfsp-tests/dirbuf-test.c index e965a2e1..7ef05c12 100644 --- a/tst/winfsp-tests/dirbuf-test.c +++ b/tst/winfsp-tests/dirbuf-test.c @@ -145,7 +145,7 @@ static void dirbuf_dots_test(void) FspFileSystemDeleteDirectoryBuffer(&DirBuffer); } -static void dirbuf_fill_dotest(unsigned seed, ULONG Count) +static void dirbuf_fill_dotest(unsigned seed, ULONG Count, ULONG InvalidCount) { PVOID DirBuffer = 0; NTSTATUS Result; @@ -202,6 +202,51 @@ static void dirbuf_fill_dotest(unsigned seed, ULONG Count) ASSERT(STATUS_SUCCESS == Result); } +#if 1 + { + ASSERT(Count > InvalidCount); + +#if !defined(FspFileSystemDirectoryBufferEntryInvalid) + const ULONG FspFileSystemDirectoryBufferEntryInvalid = ((ULONG)-1); +#endif + typedef struct + { + SRWLOCK Lock; + ULONG 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; @@ -303,19 +348,32 @@ static void dirbuf_fill_test(void) { unsigned seed = (unsigned)time(0); - dirbuf_fill_dotest(1485473509, 10); + dirbuf_fill_dotest(1485473509, 10, 0); + dirbuf_fill_dotest(1485473509, 10, 3); for (ULONG I = 0; 10000 > I; I++) - dirbuf_fill_dotest(seed + I, 10); + { + dirbuf_fill_dotest(seed + I, 10, 0); + dirbuf_fill_dotest(seed + I, 10, 3); + } for (ULONG I = 0; 1000 > I; I++) - dirbuf_fill_dotest(seed + I, 100); + { + dirbuf_fill_dotest(seed + I, 100, 0); + dirbuf_fill_dotest(seed + I, 100, 30); + } for (ULONG I = 0; 100 > I; I++) - dirbuf_fill_dotest(seed + I, 1000); + { + dirbuf_fill_dotest(seed + I, 1000, 0); + dirbuf_fill_dotest(seed + I, 1000, 300); + } for (ULONG I = 0; 10 > I; I++) - dirbuf_fill_dotest(seed + I, 10000); + { + dirbuf_fill_dotest(seed + I, 10000, 0); + dirbuf_fill_dotest(seed + I, 10000, 3000); + } } void dirbuf_tests(void)