From f97ef5704ffe061b4de9b11d1cfc3f22d488daef Mon Sep 17 00:00:00 2001 From: Bill Zissimopoulos Date: Sat, 28 Jan 2017 11:49:57 -0800 Subject: [PATCH] dll: fuse: reimplement ReadDirectory --- inc/winfsp/winfsp.h | 2 + src/dll/dirbuf.c | 12 ++ src/dll/fuse/fuse_intf.c | 355 ++++++++++++++++----------------------- src/dll/fuse/library.h | 16 +- src/dll/library.h | 3 + src/dll/posix.c | 27 +++ 6 files changed, 190 insertions(+), 225 deletions(-) diff --git a/inc/winfsp/winfsp.h b/inc/winfsp/winfsp.h index 4300d9ba..ea7f3b42 100644 --- a/inc/winfsp/winfsp.h +++ b/inc/winfsp/winfsp.h @@ -1403,6 +1403,8 @@ NTSTATUS FspPosixMapPosixToWindowsPath(const char *PosixPath, PWSTR *PWindowsPat return FspPosixMapPosixToWindowsPathEx(PosixPath, PWindowsPath, TRUE); } FSP_API VOID FspPosixDeletePath(void *Path); +FSP_API VOID FspPosixEncodeWindowsPath(PWSTR WindowsPath, ULONG Size); +FSP_API VOID FspPosixDecodeWindowsPath(PWSTR WindowsPath, ULONG Size); /* * Path Handling diff --git a/src/dll/dirbuf.c b/src/dll/dirbuf.c index 4227a71d..46029371 100644 --- a/src/dll/dirbuf.c +++ b/src/dll/dirbuf.c @@ -390,3 +390,15 @@ FSP_API VOID FspFileSystemDeleteDirectoryBuffer(PVOID *PDirBuffer) *PDirBuffer = 0; } } + +VOID FspFileSystemPeekInDirectoryBuffer(PVOID *PDirBuffer, + PUINT8 *PBuffer, PULONG *PIndex, PULONG PCount) +{ + /* assume that FspFileSystemAcquireDirectoryBuffer has been called */ + + FSP_FILE_SYSTEM_DIRECTORY_BUFFER *DirBuffer = *PDirBuffer; + + *PBuffer = DirBuffer->Buffer; + *PIndex = (PULONG)(DirBuffer->Buffer + DirBuffer->HiMark); + *PCount = (DirBuffer->Capacity - DirBuffer->HiMark) / sizeof(ULONG); +} diff --git a/src/dll/fuse/fuse_intf.c b/src/dll/fuse/fuse_intf.c index e36277c5..835f7d32 100644 --- a/src/dll/fuse/fuse_intf.c +++ b/src/dll/fuse/fuse_intf.c @@ -882,7 +882,6 @@ static NTSTATUS fsp_fuse_intf_Create(FSP_FILE_SYSTEM *FileSystem, filedesc->OpenFlags = fi.flags; filedesc->FileHandle = fi.fh; filedesc->DirBuffer = 0; - filedesc->DirBufferSize = 0; contexthdr->PosixPath = 0; Result = STATUS_SUCCESS; @@ -1000,7 +999,6 @@ static NTSTATUS fsp_fuse_intf_Open(FSP_FILE_SYSTEM *FileSystem, filedesc->OpenFlags = fi.flags; filedesc->FileHandle = fi.fh; filedesc->DirBuffer = 0; - filedesc->DirBufferSize = 0; contexthdr->PosixPath = 0; Result = STATUS_SUCCESS; @@ -1113,7 +1111,7 @@ static VOID fsp_fuse_intf_Close(FSP_FILE_SYSTEM *FileSystem, f->ops.release(filedesc->PosixPath, &fi); } - MemFree(filedesc->DirBuffer); + FspFileSystemDeleteDirectoryBuffer(&filedesc->DirBuffer); MemFree(filedesc->PosixPath); MemFree(filedesc); } @@ -1603,102 +1601,157 @@ exit: return Result; } -int fsp_fuse_intf_AddDirInfo(void *buf, const char *name, +static int fsp_fuse_intf_AddDirInfo(void *buf, const char *name, const struct fuse_stat *stbuf, fuse_off_t off) { struct fuse_dirhandle *dh = buf; - struct fsp_fuse_dirinfo *di; - ULONG len, xfersize; - - len = lstrlenA(name); - if (len > 255) - len = 255; - - di = (PVOID)((PUINT8)dh->Buffer + dh->BytesTransferred); - xfersize = FSP_FSCTL_DEFAULT_ALIGN_UP(sizeof(struct fsp_fuse_dirinfo) + len + 1); - - if ((PUINT8)di + xfersize > (PUINT8)dh->Buffer + dh->Length) - { - PVOID Buffer; - ULONG Length = dh->Length; - - if (0 == Length) - Length = 16 * 1024; - else if (Length < 16 * 1024 * 1024) - Length *= 2; - else - return 1; - - Buffer = MemAlloc(Length); - if (0 == Buffer) - return 1; - - memcpy(Buffer, dh->Buffer, dh->BytesTransferred); - MemFree(dh->Buffer); - - dh->Buffer = Buffer; - dh->Length = Length; - - di = (PVOID)((PUINT8)dh->Buffer + dh->BytesTransferred); - } - - dh->BytesTransferred += xfersize; - dh->NonZeroOffset = dh->NonZeroOffset || 0 != off; - - di->Size = (UINT16)(sizeof(struct fsp_fuse_dirinfo) + len + 1); - di->FileInfoValid = FALSE; - di->NextOffset = 0 != off ? off : dh->BytesTransferred; - memcpy(di->PosixNameBuf, name, len); - di->PosixNameBuf[len] = '\0'; - - return 0; -} - -int fsp_fuse_intf_AddDirInfoOld(fuse_dirh_t dh, const char *name, - int type, fuse_ino_t ino) -{ - return fsp_fuse_intf_AddDirInfo(dh, name, 0, 0) ? -ENOMEM : 0; -} - -static NTSTATUS fsp_fuse_intf_ReadDirectory(FSP_FILE_SYSTEM *FileSystem, - PVOID FileContext, PWSTR Pattern, PWSTR Marker, - PVOID Buffer, ULONG Length, PULONG PBytesTransferred) -{ - return STATUS_INVALID_DEVICE_REQUEST; -#if 0 - struct fuse *f = FileSystem->UserContext; - struct fsp_fuse_file_desc *filedesc = FileNode; - struct fuse_file_info fi; - struct fuse_dirhandle dh; - struct fsp_fuse_dirinfo *di; - PUINT8 diend; + struct fsp_fuse_file_desc *filedesc = dh->filedesc; union { FSP_FSCTL_DIR_INFO V; UINT8 B[sizeof(FSP_FSCTL_DIR_INFO) + 255 * sizeof(WCHAR)]; } DirInfoBuf; FSP_FSCTL_DIR_INFO *DirInfo = &DirInfoBuf.V; - UINT32 Uid, Gid, Mode; + ULONG SizeA, SizeW; + + if ('/' == filedesc->PosixPath[0] && '\0' == filedesc->PosixPath[1]) + { + /* if this is the root directory do not add the dot entries */ + + if ('.' == name[0] && ('\0' == name[1] || + ('.' == name[1] && '\0' == name[2]))) + return 0; + } + + SizeA = lstrlenA(name); + if (SizeA > 255) + /* ignore bad filenames; should we return error code? */ + return 0; + + SizeW = MultiByteToWideChar(CP_UTF8, 0, name, SizeA, DirInfo->FileNameBuf, 255); + if (0 == SizeW) + /* ignore bad filenames; should we return error code? */ + return 0; + + memset(DirInfo, 0, sizeof *DirInfo); + DirInfo->Size = (UINT16)(sizeof(FSP_FSCTL_DIR_INFO) + SizeW * sizeof(WCHAR)); + + return !FspFileSystemFillDirectoryBuffer(&filedesc->DirBuffer, DirInfo, &dh->Result); +} + +static int fsp_fuse_intf_AddDirInfoOld(fuse_dirh_t dh, const char *name, + int type, fuse_ino_t ino) +{ + return fsp_fuse_intf_AddDirInfo(dh, name, 0, 0) ? -ENOMEM : 0; +} + +static NTSTATUS fsp_fuse_intf_FixDirInfo(FSP_FILE_SYSTEM *FileSystem, + struct fsp_fuse_file_desc *filedesc) +{ char *PosixPath = 0, *PosixName, *PosixPathEnd, SavedPathChar; - PWSTR FileName = 0; - ULONG Size; + ULONG SizeA, SizeW; + PUINT8 Buffer; + PULONG Index, IndexEnd; + ULONG Count; + FSP_FSCTL_DIR_INFO *DirInfo; + UINT32 Uid, Gid, Mode; + NTSTATUS Result; + + SizeA = lstrlenA(filedesc->PosixPath); + PosixPath = MemAlloc(SizeA + 1 + 255 + 1); + if (0 == PosixPath) + { + Result = STATUS_INSUFFICIENT_RESOURCES; + goto exit; + } + + memcpy(PosixPath, filedesc->PosixPath, SizeA); + if (1 < SizeA) + /* if not root */ + PosixPath[SizeA++] = '/'; + PosixPath[SizeA] = '\0'; + PosixName = PosixPath + SizeA; + + FspFileSystemPeekInDirectoryBuffer(&filedesc->DirBuffer, &Buffer, &Index, &Count); + + for (IndexEnd = Index + Count; IndexEnd > Index; Index++) + { + DirInfo = (FSP_FSCTL_DIR_INFO *)(Buffer + *Index); + SizeW = (DirInfo->Size - sizeof *DirInfo) / sizeof(WCHAR); + + if (1 == SizeW && L'.' == DirInfo->FileNameBuf[0]) + { + PosixPathEnd = 1 < PosixName - PosixPath ? PosixName - 1 : PosixName; + SavedPathChar = *PosixPathEnd; + *PosixPathEnd = '\0'; + } + else + if (2 == SizeW && L'.' == DirInfo->FileNameBuf[0] && L'.' == DirInfo->FileNameBuf[1]) + { + PosixPathEnd = 1 < PosixName - PosixPath ? PosixName - 2 : PosixName; + while (PosixPath < PosixPathEnd && '/' != *PosixPathEnd) + PosixPathEnd--; + if (PosixPath == PosixPathEnd) + PosixPathEnd++; + SavedPathChar = *PosixPathEnd; + *PosixPathEnd = '\0'; + } + else + { + PosixPathEnd = 0; + SizeA = WideCharToMultiByte(CP_UTF8, 0, DirInfo->FileNameBuf, SizeW, PosixName, 255, 0, 0); + if (0 == SizeA) + { + /* this should never happen because we just converted using MultiByteToWideChar */ + Result = STATUS_OBJECT_NAME_INVALID; + goto exit; + } + PosixName[SizeA] = '\0'; + } + + Result = fsp_fuse_intf_GetFileInfoEx(FileSystem, PosixPath, 0, + &Uid, &Gid, &Mode, &DirInfo->FileInfo); + if (!NT_SUCCESS(Result)) + goto exit; + + if (0 != PosixPathEnd) + *PosixPathEnd = SavedPathChar; + + FspPosixDecodeWindowsPath(DirInfo->FileNameBuf, SizeW); + } + + Result = STATUS_SUCCESS; + +exit: + MemFree(PosixPath); + + return Result; +} + +static NTSTATUS fsp_fuse_intf_ReadDirectory(FSP_FILE_SYSTEM *FileSystem, + PVOID FileNode, PWSTR Pattern, PWSTR Marker, + PVOID Buffer, ULONG Length, PULONG PBytesTransferred) +{ + struct fuse *f = FileSystem->UserContext; + struct fsp_fuse_file_desc *filedesc = FileNode; + struct fuse_dirhandle dh; + struct fuse_file_info fi; int err; NTSTATUS Result; - if (!filedesc->IsDirectory) - return STATUS_ACCESS_DENIED; - - memset(&dh, 0, sizeof dh); - - if (0 == filedesc->DirBuffer) + if (FspFileSystemAcquireDirectoryBuffer(&filedesc->DirBuffer, 0 == Marker, &Result)) { + memset(&dh, 0, sizeof dh); + dh.filedesc = filedesc; + dh.Result = STATUS_SUCCESS; + if (0 != f->ops.readdir) { memset(&fi, 0, sizeof fi); fi.flags = filedesc->OpenFlags; fi.fh = filedesc->FileHandle; - err = f->ops.readdir(filedesc->PosixPath, &dh, fsp_fuse_intf_AddDirInfo, Offset, &fi); + err = f->ops.readdir(filedesc->PosixPath, &dh, fsp_fuse_intf_AddDirInfo, 0, &fi); Result = fsp_fuse_ntstatus_from_errno(f->env, err); } else if (0 != f->ops.getdir) @@ -1709,143 +1762,23 @@ static NTSTATUS fsp_fuse_intf_ReadDirectory(FSP_FILE_SYSTEM *FileSystem, else Result = STATUS_INVALID_DEVICE_REQUEST; - if (!NT_SUCCESS(Result)) - goto exit; + if (NT_SUCCESS(Result)) + { + Result = dh.Result; + if (NT_SUCCESS(Result)) + Result = fsp_fuse_intf_FixDirInfo(FileSystem, filedesc); + } - if (0 == dh.BytesTransferred) - { - /* EOF */ - *PBytesTransferred = 0; - FspFileSystemAddDirInfo(0, Buffer, Length, PBytesTransferred); - goto success; - } - else if (dh.NonZeroOffset) - { - di = (PVOID)((PUINT8)dh.Buffer + 0); - diend = (PUINT8)dh.Buffer + dh.BytesTransferred; - } - else - { - di = (PVOID)((PUINT8)dh.Buffer + Offset); - diend = (PUINT8)dh.Buffer + dh.BytesTransferred; - filedesc->DirBuffer = dh.Buffer; - filedesc->DirBufferSize = dh.BytesTransferred; - dh.Buffer = 0; - } - } - else - { - di = (PVOID)((PUINT8)filedesc->DirBuffer + Offset); - diend = (PUINT8)filedesc->DirBuffer + filedesc->DirBufferSize; + FspFileSystemReleaseDirectoryBuffer(&filedesc->DirBuffer); } - for (; - (PUINT8)di + sizeof(di->Size) <= diend; - di = (PVOID)((PUINT8)di + FSP_FSCTL_DEFAULT_ALIGN_UP(di->Size))) - { - if (sizeof(struct fsp_fuse_dirinfo) > di->Size) - break; + if (!NT_SUCCESS(Result)) + return Result; - if ('/' == filedesc->PosixPath[0] && '\0' == filedesc->PosixPath[1]) - { - /* if this is the root directory do not add the dot entries */ + FspFileSystemReadDirectoryBuffer(&filedesc->DirBuffer, + Marker, Buffer, Length, PBytesTransferred); - if ('.' == di->PosixNameBuf[0] && ('\0' == di->PosixNameBuf[1] || - ('.' == di->PosixNameBuf[1] && '\0' == di->PosixNameBuf[2]))) - continue; - } - - if (!di->FileInfoValid) - { - if (0 == PosixPath) - { - Size = lstrlenA(filedesc->PosixPath); - PosixPath = MemAlloc(Size + 1 + 255 + 1); - if (0 == PosixPath) - { - Result = STATUS_INSUFFICIENT_RESOURCES; - goto exit; - } - - memcpy(PosixPath, filedesc->PosixPath, Size); - if (1 < Size) - /* if not root */ - PosixPath[Size++] = '/'; - PosixPath[Size] = '\0'; - PosixName = PosixPath + Size; - } - - if ('.' == di->PosixNameBuf[0] && '\0' == di->PosixNameBuf[1]) - { - PosixPathEnd = 1 < PosixName - PosixPath ? PosixName - 1 : PosixName; - SavedPathChar = *PosixPathEnd; - *PosixPathEnd = '\0'; - } - else - if ('.' == di->PosixNameBuf[0] && '.' == di->PosixNameBuf[1] && '\0' == di->PosixNameBuf[2]) - { - PosixPathEnd = 1 < PosixName - PosixPath ? PosixName - 2 : PosixName; - while (PosixPath < PosixPathEnd && '/' != *PosixPathEnd) - PosixPathEnd--; - if (PosixPath == PosixPathEnd) - PosixPathEnd++; - SavedPathChar = *PosixPathEnd; - *PosixPathEnd = '\0'; - } - else - { - PosixPathEnd = 0; - Size = lstrlenA(di->PosixNameBuf); - if (Size > 255) - Size = 255; - memcpy(PosixName, di->PosixNameBuf, Size); - PosixName[Size] = '\0'; - } - - Result = fsp_fuse_intf_GetFileInfoEx(FileSystem, PosixPath, 0, - &Uid, &Gid, &Mode, &di->FileInfo); - if (!NT_SUCCESS(Result)) - goto exit; - - if (0 != PosixPathEnd) - *PosixPathEnd = SavedPathChar; - - di->FileInfoValid = TRUE; - } - memcpy(&DirInfo->FileInfo, &di->FileInfo, sizeof di->FileInfo); - - Result = FspPosixMapPosixToWindowsPath(di->PosixNameBuf, &FileName); - if (!NT_SUCCESS(Result)) - goto exit; - - Size = lstrlenW(FileName); - if (Size > 255) - Size = 255; - Size *= sizeof(WCHAR); - memcpy(DirInfo->FileNameBuf, FileName, Size); - - FspPosixDeletePath(FileName); - - memset(DirInfo->Padding, 0, sizeof DirInfo->Padding); - DirInfo->Size = (UINT16)(sizeof(FSP_FSCTL_DIR_INFO) + Size); - DirInfo->NextOffset = di->NextOffset; - - if (!FspFileSystemAddDirInfo(DirInfo, Buffer, Length, PBytesTransferred)) - break; - } - - if ((PUINT8)di + sizeof(di->Size) > diend) - FspFileSystemAddDirInfo(0, Buffer, Length, PBytesTransferred); - -success: - Result = STATUS_SUCCESS; - -exit: - MemFree(PosixPath); - MemFree(dh.Buffer); - - return Result; -#endif + return STATUS_SUCCESS; } static NTSTATUS fsp_fuse_intf_ResolveReparsePoints(FSP_FILE_SYSTEM *FileSystem, diff --git a/src/dll/fuse/library.h b/src/dll/fuse/library.h index d11070d2..11db1781 100644 --- a/src/dll/fuse/library.h +++ b/src/dll/fuse/library.h @@ -62,27 +62,15 @@ struct fsp_fuse_file_desc int OpenFlags; UINT64 FileHandle; PVOID DirBuffer; - ULONG DirBufferSize; }; struct fuse_dirhandle { - PVOID Buffer; - ULONG Length; - ULONG BytesTransferred; - BOOLEAN NonZeroOffset; + struct fsp_fuse_file_desc *filedesc; + NTSTATUS Result; BOOLEAN DotFiles, HasChild; }; -struct fsp_fuse_dirinfo -{ - UINT16 Size; - FSP_FSCTL_FILE_INFO FileInfo; - BOOLEAN FileInfoValid; - UINT64 NextOffset; - char PosixNameBuf[]; /* includes term-0 (unlike FSP_FSCTL_DIR_INFO) */ -}; - NTSTATUS fsp_fuse_op_enter(FSP_FILE_SYSTEM *FileSystem, FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response); NTSTATUS fsp_fuse_op_leave(FSP_FILE_SYSTEM *FileSystem, diff --git a/src/dll/library.h b/src/dll/library.h index f755fe3a..b3c1376c 100644 --- a/src/dll/library.h +++ b/src/dll/library.h @@ -51,6 +51,9 @@ NTSTATUS FspEventLogUnregister(VOID); PWSTR FspDiagIdent(VOID); +VOID FspFileSystemPeekInDirectoryBuffer(PVOID *PDirBuffer, + PUINT8 *PBuffer, PULONG *PIndex, PULONG PCount); + BOOL WINAPI FspServiceConsoleCtrlHandler(DWORD CtrlType); static inline ULONG FspPathSuffixIndex(PWSTR FileName) diff --git a/src/dll/posix.c b/src/dll/posix.c index fe66f22f..5640115c 100644 --- a/src/dll/posix.c +++ b/src/dll/posix.c @@ -923,3 +923,30 @@ FSP_API VOID FspPosixDeletePath(void *Path) { MemFree(Path); } + +FSP_API VOID FspPosixEncodeWindowsPath(PWSTR WindowsPath, ULONG Size) +{ + for (PWSTR p = WindowsPath, endp = p + Size; endp > p; p++) + { + WCHAR c = *p; + + if (L'\\' == c) + *p = L'/'; + /* encode characters in the Unicode private use area: U+F0XX -> XX */ + else if (0xf000 <= c && c <= 0xf0ff) + *p &= ~0xf000; + } +} + +FSP_API VOID FspPosixDecodeWindowsPath(PWSTR WindowsPath, ULONG Size) +{ + for (PWSTR p = WindowsPath, endp = p + Size; endp > p; p++) + { + WCHAR c = *p; + + if (L'/' == c) + *p = L'\\'; + else if (128 > c && (FspPosixInvalidPathChars[c >> 5] & (0x80000000 >> (c & 0x1f)))) + *p |= 0xf000; + } +}