From 27b52fd167a536b80f82fa541e72bc6505bc4cb5 Mon Sep 17 00:00:00 2001 From: Bill Zissimopoulos Date: Thu, 6 Jan 2022 13:40:26 +0000 Subject: [PATCH] tst: ntptfs: new passthrough file system --- tst/ntptfs/.gitignore | 8 + tst/ntptfs/lfs.c | 332 +++++++++ tst/ntptfs/ptfs-main.c | 275 +++++++ tst/ntptfs/ptfs.c | 1220 +++++++++++++++++++++++++++++++ tst/ntptfs/ptfs.h | 364 +++++++++ tst/ntptfs/ptfs.sln | 28 + tst/ntptfs/ptfs.vcxproj | 187 +++++ tst/ntptfs/ptfs.vcxproj.filters | 25 + tst/winfsp-tests/info-test.c | 2 +- tst/winfsp-tests/stream-tests.c | 4 +- tst/winfsp-tests/winfsp-tests.h | 5 + 11 files changed, 2447 insertions(+), 3 deletions(-) create mode 100644 tst/ntptfs/.gitignore create mode 100644 tst/ntptfs/lfs.c create mode 100644 tst/ntptfs/ptfs-main.c create mode 100644 tst/ntptfs/ptfs.c create mode 100644 tst/ntptfs/ptfs.h create mode 100644 tst/ntptfs/ptfs.sln create mode 100644 tst/ntptfs/ptfs.vcxproj create mode 100644 tst/ntptfs/ptfs.vcxproj.filters diff --git a/tst/ntptfs/.gitignore b/tst/ntptfs/.gitignore new file mode 100644 index 00000000..1f4596af --- /dev/null +++ b/tst/ntptfs/.gitignore @@ -0,0 +1,8 @@ +build +*.ncb +*.suo +*.vcproj.* +*.vcxproj.user +*.VC.db +*.VC.opendb +.vs diff --git a/tst/ntptfs/lfs.c b/tst/ntptfs/lfs.c new file mode 100644 index 00000000..fa99d4d2 --- /dev/null +++ b/tst/ntptfs/lfs.c @@ -0,0 +1,332 @@ +/** + * @file lfs.c + * + * @copyright 2015-2021 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 "ptfs.h" + +static inline HANDLE LfsThreadEvent(VOID) +{ + static __declspec(thread) HANDLE Event; + + if (0 == Event) + Event = CreateEventW(0, TRUE, FALSE, 0); + return Event; +} + +NTSTATUS LfsCreateFile( + PHANDLE PHandle, + ACCESS_MASK DesiredAccess, + HANDLE RootHandle, + PWSTR FileName, + PSECURITY_DESCRIPTOR SecurityDescriptor, + PLARGE_INTEGER AllocationSize, + ULONG FileAttributes, + ULONG CreateDisposition, + ULONG CreateOptions, + PVOID EaBuffer, + ULONG EaLength) +{ + UNICODE_STRING Ufnm; + OBJECT_ATTRIBUTES Obja; + IO_STATUS_BLOCK Iosb; + NTSTATUS Result; + + RtlInitUnicodeString(&Ufnm, FileName + 1); + InitializeObjectAttributes(&Obja, &Ufnm, 0, RootHandle, SecurityDescriptor); + + Result = NtCreateFile( + PHandle, + FILE_READ_ATTRIBUTES | DesiredAccess, + &Obja, + &Iosb, + AllocationSize, + FileAttributes, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + CreateDisposition, + CreateOptions, + EaBuffer, + EaLength); +#if 0 + if (STATUS_DELETE_PENDING == Result && IsDebuggerPresent()) + DebugBreak(); +#endif + return Result; +} + +NTSTATUS LfsOpenFile( + PHANDLE PHandle, + ACCESS_MASK DesiredAccess, + HANDLE RootHandle, + PWSTR FileName, + ULONG OpenOptions) +{ + UNICODE_STRING Ufnm; + OBJECT_ATTRIBUTES Obja; + IO_STATUS_BLOCK Iosb; + NTSTATUS Result; + + RtlInitUnicodeString(&Ufnm, FileName + 1); + InitializeObjectAttributes(&Obja, &Ufnm, 0, RootHandle, 0); + + Result = NtOpenFile( + PHandle, + FILE_READ_ATTRIBUTES | DesiredAccess, + &Obja, + &Iosb, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + OpenOptions); +#if 0 + if (STATUS_DELETE_PENDING == Result && IsDebuggerPresent()) + DebugBreak(); +#endif + return Result; +} + +NTSTATUS LfsGetFileInfo( + HANDLE Handle, + ULONG RootPrefixLength, + FSP_FSCTL_FILE_INFO *FileInfo) +{ + FSP_FSCTL_OPEN_FILE_INFO *OpenFileInfo = -1 != RootPrefixLength ? + FspFileSystemGetOpenFileInfo(FileInfo) : 0; + IO_STATUS_BLOCK Iosb; + union + { + FILE_ALL_INFORMATION V; + UINT8 B[FIELD_OFFSET(FILE_ALL_INFORMATION, NameInformation.FileName) + FSP_FSCTL_TRANSACT_PATH_SIZEMAX]; + } FileAllInfo; + FILE_ATTRIBUTE_TAG_INFORMATION FileAttrInfo; + NTSTATUS Result; + + Result = NtQueryInformationFile( + Handle, + &Iosb, + &FileAllInfo, + sizeof FileAllInfo, + 18/*FileAllInformation*/); + if (STATUS_BUFFER_OVERFLOW == Result) + OpenFileInfo = 0; + else if (!NT_SUCCESS(Result)) + return Result; + if (0 != (FILE_ATTRIBUTE_REPARSE_POINT & FileAllInfo.V.BasicInformation.FileAttributes)) + { + Result = NtQueryInformationFile( + Handle, + &Iosb, + &FileAttrInfo, + sizeof FileAttrInfo, + 35/*FileAttributeTagInformation*/); + if (!NT_SUCCESS(Result)) + return Result; + } + + Result = STATUS_SUCCESS; + + FileInfo->FileAttributes = FileAllInfo.V.BasicInformation.FileAttributes; + FileInfo->ReparseTag = 0 != (FILE_ATTRIBUTE_REPARSE_POINT & FileAllInfo.V.BasicInformation.FileAttributes) ? + FileAttrInfo.ReparseTag : 0; + FileInfo->AllocationSize = FileAllInfo.V.StandardInformation.AllocationSize.QuadPart; + FileInfo->FileSize = FileAllInfo.V.StandardInformation.EndOfFile.QuadPart; + FileInfo->CreationTime = FileAllInfo.V.BasicInformation.CreationTime.QuadPart; + FileInfo->LastAccessTime = FileAllInfo.V.BasicInformation.LastAccessTime.QuadPart; + FileInfo->LastWriteTime = FileAllInfo.V.BasicInformation.LastWriteTime.QuadPart; + FileInfo->ChangeTime = FileAllInfo.V.BasicInformation.ChangeTime.QuadPart; + FileInfo->IndexNumber = FileAllInfo.V.InternalInformation.IndexNumber.QuadPart; + FileInfo->HardLinks = 0; + FileInfo->EaSize = FileAllInfo.V.EaInformation.EaSize; + + if (0 != OpenFileInfo && + OpenFileInfo->NormalizedNameSize > sizeof(WCHAR) + FileAllInfo.V.NameInformation.FileNameLength && + RootPrefixLength <= FileAllInfo.V.NameInformation.FileNameLength) + { + PWSTR P = (PVOID)((PUINT8)FileAllInfo.V.NameInformation.FileName + RootPrefixLength); + ULONG L = FileAllInfo.V.NameInformation.FileNameLength - RootPrefixLength; + + if (L'\\' == *P) + { + memcpy(OpenFileInfo->NormalizedName, P, L); + OpenFileInfo->NormalizedNameSize = (UINT16)L; + } + else + { + *OpenFileInfo->NormalizedName = L'\\'; + memcpy(OpenFileInfo->NormalizedName + 1, P, L); + OpenFileInfo->NormalizedNameSize = (UINT16)(L + sizeof(WCHAR)); + } + } + + return Result; +} + +NTSTATUS LfsReadFile( + HANDLE Handle, + PVOID Buffer, + UINT64 Offset, + ULONG Length, + PULONG PBytesTransferred) +{ + HANDLE Event; + IO_STATUS_BLOCK Iosb; + NTSTATUS Result; + + Event = LfsThreadEvent(); + if (0 == Event) + return STATUS_INSUFFICIENT_RESOURCES; + + Result = NtReadFile( + Handle, + Event, + 0, + 0, + &Iosb, + Buffer, + Length, + (PLARGE_INTEGER)&Offset, + 0); + if (STATUS_PENDING == Result) + { + WaitForSingleObject(Event, INFINITE); + Result = Iosb.Status; + } + + *PBytesTransferred = (ULONG)Iosb.Information; + + return Result; +} + +NTSTATUS LfsWriteFile( + HANDLE Handle, + PVOID Buffer, + UINT64 Offset, + ULONG Length, + PULONG PBytesTransferred) +{ + HANDLE Event; + IO_STATUS_BLOCK Iosb; + NTSTATUS Result; + + Event = LfsThreadEvent(); + if (0 == Event) + return STATUS_INSUFFICIENT_RESOURCES; + + Result = NtWriteFile( + Handle, + Event, + 0, + 0, + &Iosb, + Buffer, + Length, + (PLARGE_INTEGER)&Offset, + 0); + if (STATUS_PENDING == Result) + { + WaitForSingleObject(Event, INFINITE); + Result = Iosb.Status; + } + + *PBytesTransferred = (ULONG)Iosb.Information; + + return Result; +} + +NTSTATUS LfsQueryDirectoryFile( + HANDLE Handle, + PVOID Buffer, + ULONG Length, + FILE_INFORMATION_CLASS FileInformationClass, + BOOLEAN ReturnSingleEntry, + PWSTR FileName, + BOOLEAN RestartScan, + PULONG PBytesTransferred) +{ + HANDLE Event; + UNICODE_STRING Ufnm; + IO_STATUS_BLOCK Iosb; + NTSTATUS Result; + + Event = LfsThreadEvent(); + if (0 == Event) + return STATUS_INSUFFICIENT_RESOURCES; + + if (0 != FileName) + RtlInitUnicodeString(&Ufnm, FileName); + + Result = NtQueryDirectoryFile( + Handle, + Event, + 0, + 0, + &Iosb, + Buffer, + Length, + FileInformationClass, + ReturnSingleEntry, + 0 != FileName ? &Ufnm : 0, + RestartScan); + if (STATUS_PENDING == Result) + { + WaitForSingleObject(Event, INFINITE); + Result = Iosb.Status; + } + + *PBytesTransferred = (ULONG)Iosb.Information; + + return Result; +} + +NTSTATUS LfsFsControlFile( + HANDLE Handle, + ULONG FsControlCode, + PVOID InputBuffer, + ULONG InputBufferLength, + PVOID OutputBuffer, + ULONG OutputBufferLength, + PULONG PBytesTransferred) +{ + HANDLE Event; + IO_STATUS_BLOCK Iosb; + NTSTATUS Result; + + Event = LfsThreadEvent(); + if (0 == Event) + return STATUS_INSUFFICIENT_RESOURCES; + + Result = NtFsControlFile( + Handle, + Event, + 0, + 0, + &Iosb, + FsControlCode, + InputBuffer, + InputBufferLength, + OutputBuffer, + OutputBufferLength); + if (STATUS_PENDING == Result) + { + WaitForSingleObject(Event, INFINITE); + Result = Iosb.Status; + } + + *PBytesTransferred = (ULONG)Iosb.Information; + + return Result; +} diff --git a/tst/ntptfs/ptfs-main.c b/tst/ntptfs/ptfs-main.c new file mode 100644 index 00000000..fd959011 --- /dev/null +++ b/tst/ntptfs/ptfs-main.c @@ -0,0 +1,275 @@ +/** + * @file ptfs-main.c + * + * @copyright 2015-2021 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 "ptfs.h" + +static NTSTATUS EnablePrivileges(PWSTR PrivilegeName, ...) +{ + va_list ap; + HANDLE Token = 0; + TOKEN_PRIVILEGES Privileges; + WCHAR WarnNames[1024], *WarnNameP = WarnNames; + NTSTATUS Result; + + if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &Token)) + { + Result = FspNtStatusFromWin32(GetLastError()); + goto exit; + } + + va_start(ap, PrivilegeName); + for (PWSTR Name = PrivilegeName; 0 != Name; Name = va_arg(ap, PWSTR)) + { + Privileges.PrivilegeCount = 1; + Privileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; + + if (!LookupPrivilegeValueW(0, Name, &Privileges.Privileges[0].Luid) || + !AdjustTokenPrivileges(Token, FALSE, &Privileges, 0, 0, 0)) + { + Result = FspNtStatusFromWin32(GetLastError()); + goto exit; + } + + if (ERROR_NOT_ALL_ASSIGNED == GetLastError()) + { + *WarnNameP++ = ' '; + size_t len = wcslen(Name); + memcpy(WarnNameP, Name, len * sizeof(WCHAR)); + WarnNameP += len; + *WarnNameP = '\0'; + } + } + va_end(ap); + + if (WarnNames != WarnNameP) + warn(L"cannot enable privileges:%s", WarnNames); + + Result = STATUS_SUCCESS; + +exit: + if (0 != Token) + CloseHandle(Token); + + return Result; +} + +static ULONG wcstol_deflt(wchar_t *w, ULONG deflt) +{ + wchar_t *endp; + ULONG ul = wcstol(w, &endp, 0); + return L'\0' != w[0] && L'\0' == *endp ? ul : deflt; +} + +static NTSTATUS SvcStart(FSP_SERVICE *Service, ULONG argc, PWSTR *argv) +{ +#define argtos(v) if (arge > ++argp) v = *argp; else goto usage +#define argtol(v) if (arge > ++argp) v = wcstol_deflt(*argp, v); else goto usage + + wchar_t **argp, **arge; + PWSTR RootPath = 0; + ULONG FileInfoTimeout = INFINITE; + ULONG FsAttributeMask = 0; + PWSTR VolumePrefix = 0; + PWSTR MountPoint = 0; + PWSTR OptionString = 0; + PWSTR DebugLogFile = 0; + ULONG DebugFlags = 0; + HANDLE DebugLogHandle = INVALID_HANDLE_VALUE; + WCHAR RootPathBuf[MAX_PATH]; + PTFS *Ptfs = 0; + NTSTATUS Result; + + for (argp = argv + 1, arge = argv + argc; arge > argp; argp++) + { + if (L'-' != argp[0][0]) + break; + switch (argp[0][1]) + { + case L'?': + goto usage; + case L'd': + argtol(DebugFlags); + break; + case L'D': + argtos(DebugLogFile); + break; + case L'm': + argtos(MountPoint); + break; + case L'o': + argtos(OptionString); + if (0 == _wcsicmp(L"ReparsePoints", OptionString)) + FsAttributeMask |= PtfsReparsePoints; + else if (0 == _wcsicmp(L"NamedStreams", OptionString)) + FsAttributeMask |= PtfsNamedStreams; + else if (0 == _wcsicmp(L"ExtendedAttributes", OptionString)) + FsAttributeMask |= PtfsExtendedAttributes; + else if (0 == _wcsicmp(L"FlushAndPurgeOnCleanup", OptionString)) + FsAttributeMask |= PtfsFlushAndPurgeOnCleanup; + else + goto usage; + break; + case L'p': + argtos(RootPath); + break; + case L't': + argtol(FileInfoTimeout); + break; + case L'u': + argtos(VolumePrefix); + break; + default: + goto usage; + } + } + + if (arge > argp) + goto usage; + + if (0 == RootPath && 0 != VolumePrefix) + { + PWSTR P; + + P = wcschr(VolumePrefix, L'\\'); + if (0 != P && L'\\' != P[1]) + { + P = wcschr(P + 1, L'\\'); + if (0 != P && + ( + (L'A' <= P[1] && P[1] <= L'Z') || + (L'a' <= P[1] && P[1] <= L'z') + ) && + L'$' == P[2]) + { + StringCbPrintf(RootPathBuf, sizeof RootPathBuf, L"%c:%s", P[1], P + 3); + RootPath = RootPathBuf; + } + } + } + + if (0 == RootPath || 0 == MountPoint) + goto usage; + + if (0 != DebugLogFile) + { + if (0 == wcscmp(L"-", DebugLogFile)) + DebugLogHandle = GetStdHandle(STD_ERROR_HANDLE); + else + DebugLogHandle = CreateFileW( + DebugLogFile, + FILE_APPEND_DATA, + FILE_SHARE_READ | FILE_SHARE_WRITE, + 0, + OPEN_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + 0); + if (INVALID_HANDLE_VALUE == DebugLogHandle) + { + fail(L"cannot open debug log file"); + goto usage; + } + + FspDebugLogSetHandle(DebugLogHandle); + } + + EnablePrivileges(SE_BACKUP_NAME, SE_RESTORE_NAME, SE_CREATE_SYMBOLIC_LINK_NAME, 0); + + Result = PtfsCreate( + RootPath, + FileInfoTimeout, + FsAttributeMask, + VolumePrefix, + MountPoint, + DebugFlags, + &Ptfs); + if (!NT_SUCCESS(Result)) + { + fail(L"cannot create file system"); + goto exit; + } + + Result = FspFileSystemStartDispatcher(Ptfs->FileSystem, 0); + if (!NT_SUCCESS(Result)) + { + fail(L"cannot start file system"); + goto exit; + } + + MountPoint = FspFileSystemMountPoint(Ptfs->FileSystem); + + info(L"%s -t %ld%s%s -p %s -m %s", + L"" PROGNAME, + FileInfoTimeout, + 0 != VolumePrefix && L'\0' != VolumePrefix[0] ? L" -u " : L"", + 0 != VolumePrefix && L'\0' != VolumePrefix[0] ? VolumePrefix : L"", + RootPath, + MountPoint); + + Service->UserContext = Ptfs; + Result = STATUS_SUCCESS; + +exit: + if (!NT_SUCCESS(Result) && 0 != Ptfs) + PtfsDelete(Ptfs); + + return Result; + +usage: + static wchar_t usage[] = L"" + "usage: %s OPTIONS\n" + "\n" + "options:\n" + " -d DebugFlags [-1: enable all debug logs]\n" + " -D DebugLogFile [file path; use - for stderr]\n" + " -t FileInfoTimeout [millis]\n" + " -o ReparsePoints\n" + " -o NamedStreams\n" + " -o ExtendedAttributes\n" + " -o FlushAndPurgeOnCleanup\n" + " -u \\Server\\Share [UNC prefix (single backslash)]\n" + " -p Directory [directory to expose as pass through file system]\n" + " -m MountPoint [X:|*|directory]\n"; + + fail(usage, L"" PROGNAME); + + return STATUS_UNSUCCESSFUL; + +#undef argtos +#undef argtol +} + +static NTSTATUS SvcStop(FSP_SERVICE *Service) +{ + PTFS *Ptfs = Service->UserContext; + + FspFileSystemStopDispatcher(Ptfs->FileSystem); + PtfsDelete(Ptfs); + + return STATUS_SUCCESS; +} + +int wmain(int argc, wchar_t **argv) +{ + if (!NT_SUCCESS(FspLoad(0))) + return ERROR_DELAY_LOAD_FAILED; + + return FspServiceRun(L"" PROGNAME, SvcStart, SvcStop, 0); +} diff --git a/tst/ntptfs/ptfs.c b/tst/ntptfs/ptfs.c new file mode 100644 index 00000000..e182f6ed --- /dev/null +++ b/tst/ntptfs/ptfs.c @@ -0,0 +1,1220 @@ +/** + * @file ptfs.c + * + * @copyright 2015-2021 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 "ptfs.h" + +#define FileSystemContext ((PTFS *)(FileSystem)->UserContext) +#define FileContextHandle (((FILE_CONTEXT *)(FileContext))->Handle) +#define FileContextIsDirectory (((FILE_CONTEXT *)(FileContext))->IsDirectory) +#define FileContextDirBuffer (&((FILE_CONTEXT *)(FileContext))->DirBuffer) + +typedef struct +{ + HANDLE Handle; + BOOLEAN IsDirectory; + PVOID DirBuffer; +} FILE_CONTEXT; + +static NTSTATUS GetReparsePointByName( + FSP_FILE_SYSTEM *FileSystem, PVOID Context, + PWSTR FileName, BOOLEAN IsDirectory, PVOID Buffer, PSIZE_T PSize); +static NTSTATUS SetDisposition( + HANDLE Handle, BOOLEAN DeleteFile); + +static NTSTATUS GetVolumeInfo(FSP_FILE_SYSTEM *FileSystem, + FSP_FSCTL_VOLUME_INFO *VolumeInfo) +{ + PTFS *Ptfs = FileSystemContext; + IO_STATUS_BLOCK Iosb; + FILE_FS_SIZE_INFORMATION FsSizeInfo; + NTSTATUS Result; + + Result = NtQueryVolumeInformationFile( + Ptfs->RootHandle, + &Iosb, + &FsSizeInfo, + sizeof FsSizeInfo, + 3/*FileFsSizeInformation*/); + if (!NT_SUCCESS(Result)) + goto exit; + + VolumeInfo->TotalSize = FsSizeInfo.TotalAllocationUnits.QuadPart * Ptfs->AllocationUnit; + VolumeInfo->FreeSize = FsSizeInfo.AvailableAllocationUnits.QuadPart * Ptfs->AllocationUnit; + + Result = STATUS_SUCCESS; + +exit: + return Result; +} + +static NTSTATUS SetVolumeLabel_(FSP_FILE_SYSTEM *FileSystem, + PWSTR VolumeLabel, + FSP_FSCTL_VOLUME_INFO *VolumeInfo) +{ + return STATUS_INVALID_DEVICE_REQUEST; +} + +static NTSTATUS GetSecurityByName(FSP_FILE_SYSTEM *FileSystem, + PWSTR FileName, PUINT32 PFileAttributes/* or ReparsePointIndex */, + PSECURITY_DESCRIPTOR SecurityDescriptor, SIZE_T *PSecurityDescriptorSize) +{ + PTFS *Ptfs = FileSystemContext; + HANDLE Handle = 0; + IO_STATUS_BLOCK Iosb; + FILE_ATTRIBUTE_TAG_INFORMATION FileAttrInfo; + ULONG SecurityDescriptorSizeNeeded; + NTSTATUS Result; + + if (0 != (FILE_SUPPORTS_REPARSE_POINTS & Ptfs->FsAttributes) && + FspFileSystemFindReparsePoint(FileSystem, GetReparsePointByName, 0, FileName, PFileAttributes)) + { + Result = STATUS_REPARSE; + goto exit; + } + + Result = LfsOpenFile( + &Handle, + READ_CONTROL, + Ptfs->RootHandle, + FileName, + FILE_OPEN_FOR_BACKUP_INTENT | FILE_OPEN_REPARSE_POINT); + if (!NT_SUCCESS(Result)) + goto exit; + + if (0 != PFileAttributes) + { + Result = NtQueryInformationFile( + Handle, + &Iosb, + &FileAttrInfo, + sizeof FileAttrInfo, + 35/*FileAttributeTagInformation*/); + if (!NT_SUCCESS(Result)) + goto exit; + + *PFileAttributes = FileAttrInfo.FileAttributes; + + /* cache FileAttributes for Open */ + FspFileSystemGetOperationContext()->Response->Rsp.Create.Opened.FileInfo.FileAttributes = + FileAttrInfo.FileAttributes; + } + + if (0 != PSecurityDescriptorSize) + { + Result = NtQuerySecurityObject( + Handle, + OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION, + SecurityDescriptor, + (ULONG)*PSecurityDescriptorSize, + &SecurityDescriptorSizeNeeded); + if (!NT_SUCCESS(Result)) + goto exit; + + *PSecurityDescriptorSize = SecurityDescriptorSizeNeeded; + } + + Result = STATUS_SUCCESS; + +exit: + if (0 != Handle) + NtClose(Handle); + + return Result; +} + +static NTSTATUS CreateEx(FSP_FILE_SYSTEM *FileSystem, + PWSTR FileName, UINT32 CreateOptions, UINT32 GrantedAccess, + UINT32 FileAttributes, PSECURITY_DESCRIPTOR SecurityDescriptor, UINT64 AllocationSize, + PVOID ExtraBuffer, ULONG ExtraLength, BOOLEAN ExtraBufferIsReparsePoint, + PVOID *PFileContext, FSP_FSCTL_FILE_INFO *FileInfo) +{ + PTFS *Ptfs = FileSystemContext; + FILE_CONTEXT *FileContext = 0; + HANDLE Handle = 0; + BOOLEAN IsDirectory = !!(CreateOptions & FILE_DIRECTORY_FILE); + UINT32 MaximumAccess = IsDirectory ? GrantedAccess : MAXIMUM_ALLOWED; + NTSTATUS Result; + + if (ExtraBufferIsReparsePoint) + { + /* no support for WSL */ + Result = STATUS_INVALID_PARAMETER; + goto exit; + } + + CreateOptions &= + FILE_DIRECTORY_FILE | + FILE_NON_DIRECTORY_FILE | + FILE_NO_EA_KNOWLEDGE; + + Result = LfsCreateFile( + &Handle, + MaximumAccess, + Ptfs->RootHandle, + FileName, + SecurityDescriptor, + 0 != AllocationSize ? (PLARGE_INTEGER)&AllocationSize : 0, + 0 != FileAttributes ? FileAttributes : FILE_ATTRIBUTE_NORMAL, + FILE_CREATE, + FILE_OPEN_FOR_BACKUP_INTENT | FILE_OPEN_REPARSE_POINT | CreateOptions, + ExtraBufferIsReparsePoint ? 0 : ExtraBuffer, + ExtraBufferIsReparsePoint ? 0 : ExtraLength); + if (!NT_SUCCESS(Result) && MAXIMUM_ALLOWED == MaximumAccess) + switch (Result) + { + case STATUS_INVALID_PARAMETER: + Result = LfsCreateFile( + &Handle, + GrantedAccess, + Ptfs->RootHandle, + FileName, + SecurityDescriptor, + 0 != AllocationSize ? (PLARGE_INTEGER)&AllocationSize : 0, + 0 != FileAttributes ? FileAttributes : FILE_ATTRIBUTE_NORMAL, + FILE_CREATE, + FILE_OPEN_FOR_BACKUP_INTENT | FILE_OPEN_REPARSE_POINT | CreateOptions, + ExtraBufferIsReparsePoint ? 0 : ExtraBuffer, + ExtraBufferIsReparsePoint ? 0 : ExtraLength); + break; + } + if (!NT_SUCCESS(Result)) + goto exit; + + Result = LfsGetFileInfo(Handle, Ptfs->RootPrefixLength, FileInfo); + if (!NT_SUCCESS(Result)) + goto exit; + + FileContext = malloc(sizeof *FileContext); + if (0 == FileContext) + { + Result = STATUS_INSUFFICIENT_RESOURCES; + goto exit; + } + + memset(FileContext, 0, sizeof *FileContext); + FileContext->Handle = Handle; + FileContext->IsDirectory = IsDirectory; + *PFileContext = FileContext; + + Result = STATUS_SUCCESS; + +exit: + if (!NT_SUCCESS(Result)) + { + if (0 != Handle) + NtClose(Handle); + + free(FileContext); + } + + return Result; +} + +static NTSTATUS Open(FSP_FILE_SYSTEM *FileSystem, + PWSTR FileName, UINT32 CreateOptions, UINT32 GrantedAccess, + PVOID *PFileContext, FSP_FSCTL_FILE_INFO *FileInfo) +{ + PTFS *Ptfs = FileSystemContext; + FILE_CONTEXT *FileContext = 0; + BOOLEAN IsDirectory = !!(FILE_ATTRIBUTE_DIRECTORY & + FspFileSystemGetOperationContext()->Response->Rsp.Create.Opened.FileInfo.FileAttributes); + UINT32 MaximumAccess = IsDirectory ? GrantedAccess : MAXIMUM_ALLOWED; + HANDLE Handle = 0; + NTSTATUS Result; + + CreateOptions &= + FILE_DIRECTORY_FILE | + FILE_NON_DIRECTORY_FILE | + FILE_NO_EA_KNOWLEDGE; + + Result = LfsOpenFile( + &Handle, + MaximumAccess, + Ptfs->RootHandle, + FileName, + FILE_OPEN_FOR_BACKUP_INTENT | FILE_OPEN_REPARSE_POINT | CreateOptions); + if (!NT_SUCCESS(Result) && MAXIMUM_ALLOWED == MaximumAccess) + switch (Result) + { + case STATUS_ACCESS_DENIED: + case STATUS_MEDIA_WRITE_PROTECTED: + case STATUS_SHARING_VIOLATION: + case STATUS_INVALID_PARAMETER: + Result = LfsOpenFile( + &Handle, + GrantedAccess, + Ptfs->RootHandle, + FileName, + FILE_OPEN_FOR_BACKUP_INTENT | FILE_OPEN_REPARSE_POINT | CreateOptions); + break; + } + if (!NT_SUCCESS(Result)) + goto exit; + + Result = LfsGetFileInfo(Handle, Ptfs->RootPrefixLength, FileInfo); + if (!NT_SUCCESS(Result)) + goto exit; + + FileContext = malloc(sizeof *FileContext); + if (0 == FileContext) + { + Result = STATUS_INSUFFICIENT_RESOURCES; + goto exit; + } + + memset(FileContext, 0, sizeof *FileContext); + FileContext->Handle = Handle; + FileContext->IsDirectory = IsDirectory; + *PFileContext = FileContext; + + Result = STATUS_SUCCESS; + +exit: + if (!NT_SUCCESS(Result)) + { + if (0 != Handle) + NtClose(Handle); + + free(FileContext); + } + + return Result; +} + +static NTSTATUS OverwriteEx(FSP_FILE_SYSTEM *FileSystem, + PVOID FileContext, UINT32 FileAttributes, BOOLEAN ReplaceFileAttributes, UINT64 AllocationSize, + PFILE_FULL_EA_INFORMATION Ea, ULONG EaLength, + FSP_FSCTL_FILE_INFO *FileInfo) +{ + HANDLE Handle = FileContextHandle; + HANDLE NewHandle; + NTSTATUS Result; + + Result = LfsCreateFile( + &NewHandle, + ReplaceFileAttributes ? DELETE : FILE_WRITE_DATA, + Handle, + L"", + 0, + 0 != AllocationSize ? (PLARGE_INTEGER)&AllocationSize : 0, + ReplaceFileAttributes ? + (0 != FileAttributes ? FileAttributes : FILE_ATTRIBUTE_NORMAL) : + FileAttributes, + ReplaceFileAttributes ? FILE_SUPERSEDE : FILE_OVERWRITE, + FILE_OPEN_FOR_BACKUP_INTENT | FILE_OPEN_REPARSE_POINT, + Ea, + EaLength); + if (!NT_SUCCESS(Result)) + goto exit; + NtClose(NewHandle); + + Result = LfsGetFileInfo(Handle, -1, FileInfo); + +exit: + return Result; +} + +static VOID Cleanup(FSP_FILE_SYSTEM *FileSystem, + PVOID FileContext, PWSTR FileName, ULONG Flags) +{ + HANDLE Handle = FileContextHandle; + + if (Flags & FspCleanupDelete) + { + SetDisposition(Handle, TRUE); + NtClose(Handle); + + /* this will make all future uses of Handle to fail with STATUS_INVALID_HANDLE */ + FileContextHandle = 0; + } +} + +static VOID Close(FSP_FILE_SYSTEM *FileSystem, + PVOID FileContext) +{ + HANDLE Handle = FileContextHandle; + + if (0 != Handle) + NtClose(Handle); + + FspFileSystemDeleteDirectoryBuffer(FileContextDirBuffer); + + free(FileContext); +} + +static NTSTATUS Read(FSP_FILE_SYSTEM *FileSystem, + PVOID FileContext, PVOID Buffer, UINT64 Offset, ULONG Length, + PULONG PBytesTransferred) +{ + HANDLE Handle = FileContextHandle; + + return LfsReadFile( + Handle, + Buffer, + Offset, + Length, + PBytesTransferred); +} + +static NTSTATUS Write(FSP_FILE_SYSTEM *FileSystem, + PVOID FileContext, PVOID Buffer, UINT64 Offset, ULONG Length, + BOOLEAN WriteToEndOfFile, BOOLEAN ConstrainedIo, + PULONG PBytesTransferred, FSP_FSCTL_FILE_INFO *FileInfo) +{ + HANDLE Handle = FileContextHandle; + IO_STATUS_BLOCK Iosb; + FILE_STANDARD_INFORMATION FileStdInfo; + NTSTATUS Result; + + if (ConstrainedIo) + { + Result = NtQueryInformationFile( + Handle, + &Iosb, + &FileStdInfo, + sizeof FileStdInfo, + 5/*FileStandardInformation*/); + if (!NT_SUCCESS(Result)) + goto exit; + + if (Offset >= (UINT64)FileStdInfo.EndOfFile.QuadPart) + return STATUS_SUCCESS; + if (Offset + Length > (UINT64)FileStdInfo.EndOfFile.QuadPart) + Length = (ULONG)((UINT64)FileStdInfo.EndOfFile.QuadPart - Offset); + } + + Result = LfsWriteFile( + Handle, + Buffer, + Offset, + Length, + PBytesTransferred); + if (!NT_SUCCESS(Result)) + goto exit; + + Result = LfsGetFileInfo(Handle, -1, FileInfo); + +exit: + return Result; +} + +static NTSTATUS Flush(FSP_FILE_SYSTEM *FileSystem, + PVOID FileContext, + FSP_FSCTL_FILE_INFO *FileInfo) +{ + /* we do not flush the whole volume, so just return SUCCESS */ + if (0 == FileContext) + return STATUS_SUCCESS; + + HANDLE Handle = FileContextHandle; + IO_STATUS_BLOCK Iosb; + NTSTATUS Result; + + Result = NtFlushBuffersFile( + Handle, + &Iosb); + if (!NT_SUCCESS(Result)) + goto exit; + + Result = LfsGetFileInfo(Handle, -1, FileInfo); + +exit: + return Result; +} + +static NTSTATUS GetFileInfo(FSP_FILE_SYSTEM *FileSystem, + PVOID FileContext, + FSP_FSCTL_FILE_INFO *FileInfo) +{ + HANDLE Handle = FileContextHandle; + + return LfsGetFileInfo(Handle, -1, FileInfo); +} + +static NTSTATUS SetBasicInfo(FSP_FILE_SYSTEM *FileSystem, + PVOID FileContext, UINT32 FileAttributes, + UINT64 CreationTime, UINT64 LastAccessTime, UINT64 LastWriteTime, UINT64 ChangeTime, + FSP_FSCTL_FILE_INFO *FileInfo) +{ + HANDLE Handle = FileContextHandle; + IO_STATUS_BLOCK Iosb; + FILE_BASIC_INFORMATION FileBasicInfo; + NTSTATUS Result; + + if (INVALID_FILE_ATTRIBUTES == FileAttributes) + FileAttributes = 0; + else if (0 == FileAttributes) + FileAttributes = FILE_ATTRIBUTE_NORMAL; + + memset(&FileBasicInfo, 0, sizeof FileBasicInfo); + FileBasicInfo.CreationTime.QuadPart = CreationTime; + FileBasicInfo.LastAccessTime.QuadPart = LastAccessTime; + FileBasicInfo.LastWriteTime.QuadPart = LastWriteTime; + FileBasicInfo.ChangeTime.QuadPart = ChangeTime; + FileBasicInfo.FileAttributes = FileAttributes; + + Result = NtSetInformationFile( + Handle, + &Iosb, + &FileBasicInfo, + sizeof FileBasicInfo, + 4/*FileBasicInformation*/); + if (!NT_SUCCESS(Result)) + goto exit; + + Result = LfsGetFileInfo(Handle, -1, FileInfo); + +exit: + return Result; +} + +static NTSTATUS SetFileSize(FSP_FILE_SYSTEM *FileSystem, + PVOID FileContext, UINT64 NewSize, BOOLEAN SetAllocationSize, + FSP_FSCTL_FILE_INFO *FileInfo) +{ + HANDLE Handle = FileContextHandle; + IO_STATUS_BLOCK Iosb; + FILE_ALLOCATION_INFORMATION FileAllocInfo; + FILE_END_OF_FILE_INFORMATION FileEofInfo; + NTSTATUS Result; + + if (SetAllocationSize) + { + FileAllocInfo.AllocationSize.QuadPart = NewSize; + + Result = NtSetInformationFile( + Handle, + &Iosb, + &FileAllocInfo, + sizeof FileAllocInfo, + 19/*FileAllocationInformation*/); + } + else + { + FileEofInfo.EndOfFile.QuadPart = NewSize; + + Result = NtSetInformationFile( + Handle, + &Iosb, + &FileEofInfo, + sizeof FileEofInfo, + 20/*FileEndOfFileInformation*/); + } + if (!NT_SUCCESS(Result)) + goto exit; + + Result = LfsGetFileInfo(Handle, -1, FileInfo); + +exit: + return Result; +} + +static NTSTATUS SetDelete(FSP_FILE_SYSTEM *FileSystem, + PVOID FileContext, PWSTR FileName, BOOLEAN DeleteFile) +{ + return SetDisposition(FileContextHandle, DeleteFile); +} + +static NTSTATUS SetDisposition( + HANDLE Handle, BOOLEAN DeleteFile) +{ + IO_STATUS_BLOCK Iosb; + FILE_DISPOSITION_INFORMATION_EX FileDispInfoEx; + FILE_DISPOSITION_INFORMATION FileDispInfo; + NTSTATUS Result; + + FileDispInfoEx.Flags = DeleteFile ? + 0x17/*DELETE | POSIX_SEMANTICS | IGNORE_READONLY_ATTRIBUTE | FORCE_IMAGE_SECTION_CHECK*/ : + 0; + + Result = NtSetInformationFile( + Handle, + &Iosb, + &FileDispInfoEx, + sizeof FileDispInfoEx, + 64/*FileDispositionInformationEx*/); + if (!NT_SUCCESS(Result)) + { + switch (Result) + { + case STATUS_ACCESS_DENIED: + case STATUS_DIRECTORY_NOT_EMPTY: + case STATUS_CANNOT_DELETE: + case STATUS_FILE_DELETED: + goto exit; + } + + FileDispInfo.DeleteFile = DeleteFile; + + Result = NtSetInformationFile( + Handle, + &Iosb, + &FileDispInfo, + sizeof FileDispInfo, + 13/*FileDispositionInformation*/); + if (!NT_SUCCESS(Result)) + goto exit; + } + + Result = STATUS_SUCCESS; + +exit: + return Result; +} + +static NTSTATUS Rename(FSP_FILE_SYSTEM *FileSystem, + PVOID FileContext, + PWSTR FileName, PWSTR NewFileName, BOOLEAN ReplaceIfExists) +{ + PTFS *Ptfs = FileSystemContext; + HANDLE Handle = FileContextHandle; + BOOLEAN PosixReplaceIfExists; + IO_STATUS_BLOCK Iosb; + union + { + FILE_RENAME_INFORMATION V; + UINT8 B[FIELD_OFFSET(FILE_RENAME_INFORMATION, FileName) + FSP_FSCTL_TRANSACT_PATH_SIZEMAX]; + } FileRenInfo; + NTSTATUS Result; + + FileRenInfo.V.RootDirectory = Ptfs->RootHandle; + FileRenInfo.V.FileNameLength = (ULONG)(wcslen(NewFileName + 1) * sizeof(WCHAR)); + if (FSP_FSCTL_TRANSACT_PATH_SIZEMAX < FileRenInfo.V.FileNameLength) + { + Result = STATUS_INVALID_PARAMETER; + goto exit; + } + memcpy(FileRenInfo.V.FileName, NewFileName + 1, FileRenInfo.V.FileNameLength); + + /* POSIX rename is able to replace existing (empty) directories; verify this is what caller wants! */ + PosixReplaceIfExists = ReplaceIfExists && (!FileContextIsDirectory || 0 != (2/**POSIX_SEMANTICS*/ & + FspFileSystemGetOperationContext()->Request->Req.SetInformation.Info.RenameEx.Flags)); + + FileRenInfo.V.Flags = (PosixReplaceIfExists ? 1/*REPLACE_IF_EXISTS*/ : 0) | + 0x42 /*POSIX_SEMANTICS | IGNORE_READONLY_ATTRIBUTE*/; + + Result = NtSetInformationFile( + Handle, + &Iosb, + &FileRenInfo, + FIELD_OFFSET(FILE_RENAME_INFORMATION, FileName) + FileRenInfo.V.FileNameLength, + 65/*FileRenameInformationEx*/); + if (!NT_SUCCESS(Result)) + { + if (STATUS_OBJECT_NAME_COLLISION == Result && ReplaceIfExists && !PosixReplaceIfExists) + { + Result = STATUS_ACCESS_DENIED; + goto exit; + } + if (STATUS_INVALID_PARAMETER != Result) + goto exit; + + FileRenInfo.V.Flags = 0; + FileRenInfo.V.ReplaceIfExists = ReplaceIfExists; + + Result = NtSetInformationFile( + Handle, + &Iosb, + &FileRenInfo, + FIELD_OFFSET(FILE_RENAME_INFORMATION, FileName) + FileRenInfo.V.FileNameLength, + 10/*FileRenameInformation*/); + if (!NT_SUCCESS(Result)) + goto exit; + } + + Result = STATUS_SUCCESS; + +exit: + return Result; +} + +static NTSTATUS GetSecurity(FSP_FILE_SYSTEM *FileSystem, + PVOID FileContext, + PSECURITY_DESCRIPTOR SecurityDescriptor, SIZE_T *PSecurityDescriptorSize) +{ + HANDLE Handle = FileContextHandle; + ULONG SecurityDescriptorSizeNeeded; + NTSTATUS Result; + + Result = NtQuerySecurityObject( + Handle, + OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION, + SecurityDescriptor, + (ULONG)*PSecurityDescriptorSize, + &SecurityDescriptorSizeNeeded); + if (!NT_SUCCESS(Result)) + goto exit; + + *PSecurityDescriptorSize = SecurityDescriptorSizeNeeded; + + Result = STATUS_SUCCESS; + +exit: + return Result; +} + +static NTSTATUS SetSecurity(FSP_FILE_SYSTEM *FileSystem, + PVOID FileContext, + SECURITY_INFORMATION SecurityInformation, PSECURITY_DESCRIPTOR ModificationDescriptor) +{ + HANDLE Handle = FileContextHandle; + + return NtSetSecurityObject(Handle, SecurityInformation, ModificationDescriptor); +} + +static inline VOID CopyQueryInfoToDirInfo( + FILE_ID_BOTH_DIR_INFORMATION *QueryInfo, + FSP_FSCTL_DIR_INFO *DirInfo) +{ + memset(DirInfo, 0, sizeof *DirInfo); + DirInfo->Size = (UINT16)(FIELD_OFFSET(FSP_FSCTL_DIR_INFO, FileNameBuf) + + QueryInfo->FileNameLength); + DirInfo->FileInfo.FileAttributes = QueryInfo->FileAttributes; + DirInfo->FileInfo.ReparseTag = 0 != (FILE_ATTRIBUTE_REPARSE_POINT & QueryInfo->FileAttributes) ? + QueryInfo->EaSize : 0; + DirInfo->FileInfo.AllocationSize = QueryInfo->AllocationSize.QuadPart; + DirInfo->FileInfo.FileSize = QueryInfo->EndOfFile.QuadPart; + DirInfo->FileInfo.CreationTime = QueryInfo->CreationTime.QuadPart; + DirInfo->FileInfo.LastAccessTime = QueryInfo->LastAccessTime.QuadPart; + DirInfo->FileInfo.LastWriteTime = QueryInfo->LastWriteTime.QuadPart; + DirInfo->FileInfo.ChangeTime = QueryInfo->ChangeTime.QuadPart; + DirInfo->FileInfo.IndexNumber = QueryInfo->FileId.QuadPart; + DirInfo->FileInfo.HardLinks = 0; + DirInfo->FileInfo.EaSize = 0 != (FILE_ATTRIBUTE_REPARSE_POINT & QueryInfo->FileAttributes) ? + 0 : QueryInfo->EaSize; +} + +static NTSTATUS BufferedReadDirectory(FSP_FILE_SYSTEM *FileSystem, + PVOID FileContext, PWSTR Pattern, PWSTR Marker, + PVOID Buffer, ULONG Length, PULONG PBytesTransferred) +{ + PTFS *Ptfs = FileSystemContext; + HANDLE Handle = FileContextHandle; + PVOID PDirBuffer = FileContextDirBuffer; + BOOLEAN RestartScan; + ULONG BytesTransferred; + UINT8 QueryBuffer[16 * 1024]; + FILE_ID_BOTH_DIR_INFORMATION *QueryInfo; + ULONG QueryNext; + union + { + FSP_FSCTL_DIR_INFO V; + UINT8 B[FIELD_OFFSET(FSP_FSCTL_DIR_INFO, FileNameBuf) + 255 * sizeof(WCHAR)]; + } DirInfo; + NTSTATUS Result, DirBufferResult; + + DirBufferResult = STATUS_SUCCESS; + if (FspFileSystemAcquireDirectoryBuffer(PDirBuffer, 0 == Marker, &DirBufferResult)) + { + for (RestartScan = TRUE;; RestartScan = FALSE) + { + Result = LfsQueryDirectoryFile( + Handle, + QueryBuffer, + sizeof QueryBuffer, + 37/*FileIdBothDirectoryInformation*/, + FALSE, + Pattern, + RestartScan, + &BytesTransferred); + if (!NT_SUCCESS(Result)) + break; + + for (QueryInfo = (FILE_ID_BOTH_DIR_INFORMATION *)QueryBuffer; + ; + QueryInfo = (FILE_ID_BOTH_DIR_INFORMATION *)((PUINT8)QueryInfo + QueryNext)) + { + QueryNext = QueryInfo->NextEntryOffset; + + CopyQueryInfoToDirInfo(QueryInfo, &DirInfo.V); + memcpy(DirInfo.V.FileNameBuf, QueryInfo->FileName, QueryInfo->FileNameLength); + + if (!FspFileSystemFillDirectoryBuffer(PDirBuffer, &DirInfo.V, &DirBufferResult)) + goto done; + + if (0 == QueryNext) + break; + } + } + + done: + FspFileSystemReleaseDirectoryBuffer(PDirBuffer); + } + + if (!NT_SUCCESS(DirBufferResult)) + return DirBufferResult; + + FspFileSystemReadDirectoryBuffer(PDirBuffer, + Marker, Buffer, Length, PBytesTransferred); + + return STATUS_SUCCESS; +} + +static NTSTATUS ResolveReparsePoints(FSP_FILE_SYSTEM *FileSystem, + PWSTR FileName, UINT32 ReparsePointIndex, BOOLEAN ResolveLastPathComponent, + PIO_STATUS_BLOCK PIoStatus, PVOID Buffer, PSIZE_T PSize) +{ + return FspFileSystemResolveReparsePoints(FileSystem, GetReparsePointByName, 0, + FileName, ReparsePointIndex, ResolveLastPathComponent, + PIoStatus, Buffer, PSize); +} + +static NTSTATUS GetReparsePointByName( + FSP_FILE_SYSTEM *FileSystem, PVOID Context, + PWSTR FileName, BOOLEAN IsDirectory, PVOID Buffer, PSIZE_T PSize) +{ + PTFS *Ptfs = FileSystemContext; + HANDLE Handle = 0; + union + { + REPARSE_DATA_BUFFER V; + UINT8 B[FSP_FSCTL_TRANSACT_RSP_BUFFER_SIZEMAX]; + } ReparseBuffer; + SIZE_T ReparseBufferSize; + ULONG BytesTransferred; + NTSTATUS Result; + + if (0 == Buffer) + { + Buffer = &ReparseBuffer; + PSize = &ReparseBufferSize; + ReparseBufferSize = sizeof ReparseBuffer; + } + + Result = LfsOpenFile( + &Handle, + 0, + Ptfs->RootHandle, + FileName, + FILE_OPEN_FOR_BACKUP_INTENT | FILE_OPEN_REPARSE_POINT | + (IsDirectory ? FILE_DIRECTORY_FILE : 0)); + if (!NT_SUCCESS(Result)) + goto exit; + + Result = LfsFsControlFile( + Handle, + FSCTL_GET_REPARSE_POINT, + 0, + 0, + Buffer, + (ULONG)*PSize, + &BytesTransferred); + if (STATUS_BUFFER_OVERFLOW == Result) + { + Result = STATUS_BUFFER_TOO_SMALL; + goto exit; + } + else if (!NT_SUCCESS(Result)) + goto exit; + + *PSize = BytesTransferred; + + Result = STATUS_SUCCESS; + +exit: + if (0 != Handle) + NtClose(Handle); + + return Result; +} + +static NTSTATUS GetReparsePoint(FSP_FILE_SYSTEM *FileSystem, + PVOID FileContext, + PWSTR FileName, PVOID Buffer, PSIZE_T PSize) +{ + HANDLE Handle = FileContextHandle; + ULONG BytesTransferred; + NTSTATUS Result; + + Result = LfsFsControlFile( + Handle, + FSCTL_GET_REPARSE_POINT, + 0, + 0, + Buffer, + (ULONG)*PSize, + &BytesTransferred); + if (STATUS_BUFFER_OVERFLOW == Result) + { + Result = STATUS_BUFFER_TOO_SMALL; + goto exit; + } + else if (!NT_SUCCESS(Result)) + goto exit; + + *PSize = BytesTransferred; + + Result = STATUS_SUCCESS; + +exit: + return Result; +} + +static NTSTATUS SetReparsePoint(FSP_FILE_SYSTEM *FileSystem, + PVOID FileContext, + PWSTR FileName, PVOID Buffer, SIZE_T Size) +{ + HANDLE Handle = FileContextHandle; + ULONG BytesTransferred; + NTSTATUS Result; + + Result = LfsFsControlFile( + Handle, + FSCTL_SET_REPARSE_POINT, + Buffer, + (ULONG)Size, + 0, + 0, + &BytesTransferred); + if (!NT_SUCCESS(Result)) + goto exit; + + Result = STATUS_SUCCESS; + +exit: + return Result; +} + +static NTSTATUS DeleteReparsePoint(FSP_FILE_SYSTEM *FileSystem, + PVOID FileContext, + PWSTR FileName, PVOID Buffer, SIZE_T Size) +{ + HANDLE Handle = FileContextHandle; + ULONG BytesTransferred; + NTSTATUS Result; + + Result = LfsFsControlFile( + Handle, + FSCTL_DELETE_REPARSE_POINT, + Buffer, + (ULONG)Size, + 0, + 0, + &BytesTransferred); + if (!NT_SUCCESS(Result)) + goto exit; + + Result = STATUS_SUCCESS; + +exit: + return Result; +} + +static NTSTATUS GetStreamInfo(FSP_FILE_SYSTEM *FileSystem, + PVOID FileContext, PVOID Buffer, ULONG Length, + PULONG PBytesTransferred) +{ + HANDLE Handle = FileContextHandle; + IO_STATUS_BLOCK Iosb; + UINT8 QueryBuffer[16 * 1024]; + FILE_STREAM_INFORMATION *QueryInfo; + ULONG QueryNext; + union + { + FSP_FSCTL_STREAM_INFO V; + UINT8 B[FIELD_OFFSET(FSP_FSCTL_STREAM_INFO, StreamNameBuf) + 255 * sizeof(WCHAR)]; + } StreamInfo; + PWCHAR P, EndP, NameP; + NTSTATUS Result; + + Result = NtQueryInformationFile( + Handle, + &Iosb, + QueryBuffer, + sizeof QueryBuffer, + 22/*FileStreamInformation*/); + if (!NT_SUCCESS(Result) && STATUS_BUFFER_OVERFLOW != Result) + goto exit; + + for (QueryInfo = (FILE_STREAM_INFORMATION *)QueryBuffer; + ; + QueryInfo = (FILE_STREAM_INFORMATION *)((PUINT8)QueryInfo + QueryNext)) + { + if (Iosb.Information <= (ULONG)((PUINT8)QueryInfo - QueryBuffer)) + break; + + QueryNext = QueryInfo->NextEntryOffset; + + NameP = 0; + for (P = QueryInfo->StreamName, EndP = P + QueryInfo->StreamNameLength / sizeof(WCHAR);; P++) + if (L':' == *P) + { + if (0 == NameP) + NameP = P + 1; + else + break; + } + if (0 == NameP) + NameP = P; + + memset(&StreamInfo.V, 0, sizeof StreamInfo.V); + StreamInfo.V.Size = (UINT16)(FIELD_OFFSET(FSP_FSCTL_STREAM_INFO, StreamNameBuf) + + ((PUINT8)P - (PUINT8)NameP)); + StreamInfo.V.StreamSize = QueryInfo->StreamSize.QuadPart; + StreamInfo.V.StreamAllocationSize = QueryInfo->StreamAllocationSize.QuadPart; + memcpy(StreamInfo.V.StreamNameBuf, NameP, ((PUINT8)P - (PUINT8)NameP)); + + if (!FspFileSystemAddStreamInfo(&StreamInfo.V, Buffer, Length, PBytesTransferred)) + goto done; + + if (0 == QueryNext) + break; + } + + FspFileSystemAddStreamInfo(0, Buffer, Length, PBytesTransferred); + +done: + + Result = STATUS_SUCCESS; + +exit: + return Result; +} + +static NTSTATUS GetEa(FSP_FILE_SYSTEM *FileSystem, + PVOID FileContext, + PFILE_FULL_EA_INFORMATION Ea, ULONG EaLength, PULONG PBytesTransferred) +{ + HANDLE Handle = FileContextHandle; + IO_STATUS_BLOCK Iosb; + NTSTATUS Result; + + Result = NtQueryEaFile( + Handle, + &Iosb, + Ea, + EaLength, + FALSE, + 0, + 0, + 0, + TRUE); + if (!NT_SUCCESS(Result) && STATUS_BUFFER_OVERFLOW != Result) + { + /* on error report an empty EA list */ + Iosb.Information = 0; + } + + *PBytesTransferred = (ULONG)Iosb.Information; + + Result = STATUS_SUCCESS; + + return Result; +} + +static NTSTATUS SetEa(FSP_FILE_SYSTEM *FileSystem, + PVOID FileContext, + PFILE_FULL_EA_INFORMATION Ea, ULONG EaLength, + FSP_FSCTL_FILE_INFO *FileInfo) +{ + HANDLE Handle = FileContextHandle; + IO_STATUS_BLOCK Iosb; + + return NtSetEaFile( + Handle, + &Iosb, + Ea, + EaLength); +} + +static FSP_FILE_SYSTEM_INTERFACE PtfsInterface = +{ + .GetVolumeInfo = GetVolumeInfo, + .SetVolumeLabel = SetVolumeLabel_, + .GetSecurityByName = GetSecurityByName, + .CreateEx = CreateEx, + .Open = Open, + .OverwriteEx = OverwriteEx, + .Cleanup = Cleanup, + .Close = Close, + .Read = Read, + .Write = Write, + .Flush = Flush, + .GetFileInfo = GetFileInfo, + .SetBasicInfo = SetBasicInfo, + .SetFileSize = SetFileSize, + .SetDelete = SetDelete, + .Rename = Rename, + .GetSecurity = GetSecurity, + .SetSecurity = SetSecurity, + .ReadDirectory = BufferedReadDirectory, + .ResolveReparsePoints = ResolveReparsePoints, + .GetReparsePoint = GetReparsePoint, + .SetReparsePoint = SetReparsePoint, + .DeleteReparsePoint = DeleteReparsePoint, + .GetStreamInfo = GetStreamInfo, + .GetEa = GetEa, + .SetEa = SetEa, +}; + +NTSTATUS PtfsCreate( + PWSTR RootPath, + ULONG FileInfoTimeout, + ULONG FsAttributeMask, + PWSTR VolumePrefix, + PWSTR MountPoint, + UINT32 DebugFlags, + PTFS **PPtfs) +{ + PTFS *Ptfs = 0; + FSP_FILE_SYSTEM *FileSystem = 0; + HANDLE RootHandle = INVALID_HANDLE_VALUE; + IO_STATUS_BLOCK Iosb; + union + { + FILE_FS_ATTRIBUTE_INFORMATION V; + UINT8 B[FIELD_OFFSET(FILE_FS_ATTRIBUTE_INFORMATION, FileSystemName) + MAX_PATH * sizeof(WCHAR)]; + } FsAttrInfo; + FILE_FS_SIZE_INFORMATION FsSizeInfo; + FILE_ALL_INFORMATION FileAllInfo; + FSP_FSCTL_VOLUME_PARAMS VolumeParams; + NTSTATUS Result; + + *PPtfs = 0; + + RootHandle = CreateFileW( + RootPath, + FILE_READ_ATTRIBUTES, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + 0, + OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, + 0); + if (INVALID_HANDLE_VALUE == RootHandle) + { + Result = FspNtStatusFromWin32(GetLastError()); + goto exit; + } + + Result = NtQueryVolumeInformationFile( + RootHandle, + &Iosb, + &FsAttrInfo, + sizeof FsAttrInfo, + 5/*FileFsAttributeInformation*/); + if (!NT_SUCCESS(Result)) + goto exit; + Result = NtQueryVolumeInformationFile( + RootHandle, + &Iosb, + &FsSizeInfo, + sizeof FsSizeInfo, + 3/*FileFsSizeInformation*/); + if (!NT_SUCCESS(Result)) + goto exit; + Result = NtQueryInformationFile( + RootHandle, + &Iosb, + &FileAllInfo, + sizeof FileAllInfo, + 18/*FileAllInformation*/); + if (!NT_SUCCESS(Result) && STATUS_BUFFER_OVERFLOW != Result) + goto exit; + + FsAttributeMask &= PtfsAttributesMask; + + memset(&VolumeParams, 0, sizeof VolumeParams); + VolumeParams.SectorSize = (UINT16)FsSizeInfo.BytesPerSector; + VolumeParams.SectorsPerAllocationUnit = (UINT16)FsSizeInfo.SectorsPerAllocationUnit; + VolumeParams.MaxComponentLength = (UINT16)FsAttrInfo.V.MaximumComponentNameLength; + VolumeParams.VolumeCreationTime = FileAllInfo.BasicInformation.CreationTime.QuadPart; + VolumeParams.VolumeSerialNumber = 0; + VolumeParams.FileInfoTimeout = FileInfoTimeout; + VolumeParams.CaseSensitiveSearch = 0; + VolumeParams.CasePreservedNames = !!(FsAttrInfo.V.FileSystemAttributes & FILE_CASE_PRESERVED_NAMES); + VolumeParams.UnicodeOnDisk = !!(FsAttrInfo.V.FileSystemAttributes & FILE_UNICODE_ON_DISK); + VolumeParams.PersistentAcls = !!(FsAttrInfo.V.FileSystemAttributes & FILE_PERSISTENT_ACLS); + VolumeParams.ReparsePoints = (FsAttributeMask & PtfsReparsePoints) ? + !!(FsAttrInfo.V.FileSystemAttributes & FILE_SUPPORTS_REPARSE_POINTS) : 0; + VolumeParams.NamedStreams = (FsAttributeMask & PtfsNamedStreams) ? + !!(FsAttrInfo.V.FileSystemAttributes & FILE_NAMED_STREAMS) : 0; + VolumeParams.ExtendedAttributes = (FsAttributeMask & PtfsExtendedAttributes) ? + !!(FsAttrInfo.V.FileSystemAttributes & FILE_SUPPORTS_EXTENDED_ATTRIBUTES) : 0; + VolumeParams.SupportsPosixUnlinkRename = !!(FsAttrInfo.V.FileSystemAttributes & FILE_SUPPORTS_POSIX_UNLINK_RENAME); + VolumeParams.ReadOnlyVolume = !!(FsAttrInfo.V.FileSystemAttributes & FILE_READ_ONLY_VOLUME); + VolumeParams.PostCleanupWhenModifiedOnly = 1; + VolumeParams.PassQueryDirectoryPattern = 1; + VolumeParams.FlushAndPurgeOnCleanup = (FsAttributeMask & PtfsFlushAndPurgeOnCleanup) ? + 1 : 0; + VolumeParams.AllowOpenInKernelMode = 1; + VolumeParams.RejectIrpPriorToTransact0 = 1; + VolumeParams.UmFileContextIsUserContext2 = 1; + if (0 != VolumePrefix) + wcscpy_s(VolumeParams.Prefix, sizeof VolumeParams.Prefix / sizeof(WCHAR), VolumePrefix); + memcpy(VolumeParams.FileSystemName, FsAttrInfo.V.FileSystemName, + sizeof VolumeParams.FileSystemName <= FsAttrInfo.V.FileSystemNameLength ? + sizeof VolumeParams.FileSystemName : FsAttrInfo.V.FileSystemNameLength); + + Result = FspFileSystemCreate( + VolumeParams.Prefix[0] ? + L"" FSP_FSCTL_NET_DEVICE_NAME : L"" FSP_FSCTL_DISK_DEVICE_NAME, + &VolumeParams, + &PtfsInterface, + &FileSystem); + if (!NT_SUCCESS(Result)) + goto exit; + FspFileSystemSetDebugLog(FileSystem, DebugFlags); + + Ptfs = malloc(sizeof *Ptfs); + if (0 == Ptfs) + { + Result = STATUS_INSUFFICIENT_RESOURCES; + goto exit; + } + memset(Ptfs, 0, sizeof *Ptfs); + + Ptfs->FileSystem = FileSystem; + Ptfs->RootHandle = RootHandle; + Ptfs->RootPrefixLength = FileAllInfo.NameInformation.FileNameLength; + Ptfs->FsAttributes = FsAttrInfo.V.FileSystemAttributes; + Ptfs->AllocationUnit = VolumeParams.SectorSize * VolumeParams.SectorsPerAllocationUnit; + FileSystem->UserContext = Ptfs; + + Result = FspFileSystemSetMountPoint(FileSystem, MountPoint); + if (!NT_SUCCESS(Result)) + goto exit; + + *PPtfs = Ptfs; + + Result = STATUS_SUCCESS; + +exit: + if (!NT_SUCCESS(Result)) + { + free(Ptfs); + + if (0 != FileSystem) + FspFileSystemDelete(FileSystem); + + if (INVALID_HANDLE_VALUE != RootHandle) + CloseHandle(RootHandle); + } + + return Result; +} + +VOID PtfsDelete(PTFS *Ptfs) +{ + FspFileSystemDelete(Ptfs->FileSystem); + CloseHandle(Ptfs->RootHandle); + + free(Ptfs); +} diff --git a/tst/ntptfs/ptfs.h b/tst/ntptfs/ptfs.h new file mode 100644 index 00000000..466d572d --- /dev/null +++ b/tst/ntptfs/ptfs.h @@ -0,0 +1,364 @@ +/** + * @file ptfs.h + * + * @copyright 2015-2021 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. + */ + +#ifndef PTFS_H_INCLUDED +#define PTFS_H_INCLUDED + +#include +#include + +#define PROGNAME "ptfs" + +#define info(format, ...) FspServiceLog(EVENTLOG_INFORMATION_TYPE, format, __VA_ARGS__) +#define warn(format, ...) FspServiceLog(EVENTLOG_WARNING_TYPE, format, __VA_ARGS__) +#define fail(format, ...) FspServiceLog(EVENTLOG_ERROR_TYPE, format, __VA_ARGS__) + + +/* + * PTFS + */ + +enum +{ + PtfsReparsePoints = 0x00000010, + PtfsNamedStreams = 0x00000040, + PtfsExtendedAttributes = 0x00000100, + PtfsFlushAndPurgeOnCleanup = 0x00004000, + PtfsAttributesMask = + PtfsReparsePoints | + PtfsNamedStreams | + PtfsExtendedAttributes | + PtfsFlushAndPurgeOnCleanup, +}; +typedef struct +{ + FSP_FILE_SYSTEM *FileSystem; + HANDLE RootHandle; + ULONG RootPrefixLength; + ULONG FsAttributes; + UINT64 AllocationUnit; +} PTFS; +NTSTATUS PtfsCreate( + PWSTR RootPath, + ULONG FileInfoTimeout, + ULONG FsAttributeMask, + PWSTR VolumePrefix, + PWSTR MountPoint, + UINT32 DebugFlags, + PTFS **PPtfs); +VOID PtfsDelete(PTFS *Ptfs); + + +/* + * Lower file system + */ + +NTSTATUS LfsCreateFile( + PHANDLE PHandle, + ACCESS_MASK DesiredAccess, + HANDLE RootHandle, + PWSTR FileName, + PSECURITY_DESCRIPTOR SecurityDescriptor, + PLARGE_INTEGER AllocationSize, + ULONG FileAttributes, + ULONG CreateDisposition, + ULONG CreateOptions, + PVOID EaBuffer, + ULONG EaLength); +NTSTATUS LfsOpenFile( + PHANDLE PHandle, + ACCESS_MASK DesiredAccess, + HANDLE RootHandle, + PWSTR FileName, + ULONG OpenOptions); +NTSTATUS LfsGetFileInfo( + HANDLE Handle, + ULONG RootPrefixLength, + FSP_FSCTL_FILE_INFO *FileInfo); +NTSTATUS LfsReadFile( + HANDLE Handle, + PVOID Buffer, + UINT64 Offset, + ULONG Length, + PULONG PBytesTransferred); +NTSTATUS LfsWriteFile( + HANDLE Handle, + PVOID Buffer, + UINT64 Offset, + ULONG Length, + PULONG PBytesTransferred); +NTSTATUS LfsQueryDirectoryFile( + HANDLE Handle, + PVOID Buffer, + ULONG Length, + FILE_INFORMATION_CLASS FileInformationClass, + BOOLEAN ReturnSingleEntry, + PWSTR FileName, + BOOLEAN RestartScan, + PULONG PBytesTransferred); +NTSTATUS LfsFsControlFile( + HANDLE Handle, + ULONG FsControlCode, + PVOID InputBuffer, + ULONG InputBufferLength, + PVOID OutputBuffer, + ULONG OutputBufferLength, + PULONG PBytesTransferred); + + +/* + * Missing NTDLL definitions + */ + +typedef struct +{ + ACCESS_MASK AccessFlags; +} FILE_ACCESS_INFORMATION; +typedef struct +{ + ULONG AlignmentRequirement; +} FILE_ALIGNMENT_INFORMATION; +typedef struct +{ + LARGE_INTEGER AllocationSize; +} FILE_ALLOCATION_INFORMATION; +typedef struct +{ + ULONG FileAttributes; + ULONG ReparseTag; +} FILE_ATTRIBUTE_TAG_INFORMATION; +typedef struct +{ + LARGE_INTEGER CreationTime; + LARGE_INTEGER LastAccessTime; + LARGE_INTEGER LastWriteTime; + LARGE_INTEGER ChangeTime; + ULONG FileAttributes; +} FILE_BASIC_INFORMATION; +typedef struct +{ + BOOLEAN DeleteFile; +} FILE_DISPOSITION_INFORMATION; +typedef struct +{ + ULONG Flags; +} FILE_DISPOSITION_INFORMATION_EX; +typedef struct +{ + ULONG EaSize; +} FILE_EA_INFORMATION; +typedef struct +{ + LARGE_INTEGER EndOfFile; +} FILE_END_OF_FILE_INFORMATION; +typedef struct +{ + LARGE_INTEGER IndexNumber; +} FILE_INTERNAL_INFORMATION; +typedef struct +{ + ULONG Mode; +} FILE_MODE_INFORMATION; +typedef struct +{ + ULONG FileNameLength; + WCHAR FileName[1]; +} FILE_NAME_INFORMATION; +typedef struct +{ + LARGE_INTEGER CurrentByteOffset; +} FILE_POSITION_INFORMATION; +typedef struct +{ + union + { + BOOLEAN ReplaceIfExists; + ULONG Flags; + } DUMMYUNIONNAME; + HANDLE RootDirectory; + ULONG FileNameLength; + WCHAR FileName[1]; +} FILE_RENAME_INFORMATION; +typedef struct +{ + LARGE_INTEGER AllocationSize; + LARGE_INTEGER EndOfFile; + ULONG NumberOfLinks; + BOOLEAN DeletePending; + BOOLEAN Directory; +} FILE_STANDARD_INFORMATION; +typedef struct +{ + ULONG NextEntryOffset; + ULONG StreamNameLength; + LARGE_INTEGER StreamSize; + LARGE_INTEGER StreamAllocationSize; + WCHAR StreamName[1]; +} FILE_STREAM_INFORMATION; +typedef struct +{ + FILE_BASIC_INFORMATION BasicInformation; + FILE_STANDARD_INFORMATION StandardInformation; + FILE_INTERNAL_INFORMATION InternalInformation; + FILE_EA_INFORMATION EaInformation; + FILE_ACCESS_INFORMATION AccessInformation; + FILE_POSITION_INFORMATION PositionInformation; + FILE_MODE_INFORMATION ModeInformation; + FILE_ALIGNMENT_INFORMATION AlignmentInformation; + FILE_NAME_INFORMATION NameInformation; +} FILE_ALL_INFORMATION; + +typedef struct +{ + ULONG NextEntryOffset; + ULONG FileIndex; + LARGE_INTEGER CreationTime; + LARGE_INTEGER LastAccessTime; + LARGE_INTEGER LastWriteTime; + LARGE_INTEGER ChangeTime; + LARGE_INTEGER EndOfFile; + LARGE_INTEGER AllocationSize; + ULONG FileAttributes; + ULONG FileNameLength; + ULONG EaSize; + CCHAR ShortNameLength; + WCHAR ShortName[12]; + LARGE_INTEGER FileId; + WCHAR FileName[1]; +} FILE_ID_BOTH_DIR_INFORMATION; + +typedef struct +{ + ULONG FileSystemAttributes; + LONG MaximumComponentNameLength; + ULONG FileSystemNameLength; + WCHAR FileSystemName[1]; +} FILE_FS_ATTRIBUTE_INFORMATION; +typedef struct +{ + LARGE_INTEGER TotalAllocationUnits; + LARGE_INTEGER AvailableAllocationUnits; + ULONG SectorsPerAllocationUnit; + ULONG BytesPerSector; +} FILE_FS_SIZE_INFORMATION; + +NTSTATUS NTAPI NtFlushBuffersFile( + HANDLE FileHandle, + PIO_STATUS_BLOCK IoStatusBlock); + +NTSTATUS NTAPI NtFsControlFile( + HANDLE FileHandle, + HANDLE Event, + PIO_APC_ROUTINE ApcRoutine, + PVOID ApcContext, + PIO_STATUS_BLOCK IoStatusBlock, + ULONG FsControlCode, + PVOID InputBuffer, + ULONG InputBufferLength, + PVOID OutputBuffer, + ULONG OutputBufferLength); + +NTSTATUS NTAPI NtQueryEaFile( + HANDLE FileHandle, + PIO_STATUS_BLOCK IoStatusBlock, + PVOID Buffer, + ULONG Length, + BOOLEAN ReturnSingleEntry, + PVOID EaList, + ULONG EaListLength, + PULONG EaIndex, + BOOLEAN RestartScan); + +NTSTATUS NTAPI NtQueryDirectoryFile( + HANDLE FileHandle, + HANDLE Event, + PIO_APC_ROUTINE ApcRoutine, + PVOID ApcContext, + PIO_STATUS_BLOCK IoStatusBlock, + PVOID FileInformation, + ULONG Length, + FILE_INFORMATION_CLASS FileInformationClass, + BOOLEAN ReturnSingleEntry, + PUNICODE_STRING FileName, + BOOLEAN RestartScan); + +NTSTATUS NTAPI NtQueryInformationFile( + HANDLE FileHandle, + PIO_STATUS_BLOCK IoStatusBlock, + PVOID FileInformation, + ULONG Length, + FILE_INFORMATION_CLASS FileInformationClass); + +NTSTATUS NTAPI NtQuerySecurityObject( + HANDLE Handle, + SECURITY_INFORMATION SecurityInformation, + PSECURITY_DESCRIPTOR SecurityDescriptor, + ULONG Length, + PULONG LengthNeeded); + +NTSTATUS NTAPI NtQueryVolumeInformationFile( + HANDLE FileHandle, + PIO_STATUS_BLOCK IoStatusBlock, + PVOID FsInformation, + ULONG Length, + ULONG FsInformationClass); + +NTSTATUS NTAPI NtReadFile( + HANDLE FileHandle, + HANDLE Event, + PIO_APC_ROUTINE ApcRoutine, + PVOID ApcContext, + PIO_STATUS_BLOCK IoStatusBlock, + PVOID Buffer, + ULONG Length, + PLARGE_INTEGER ByteOffset, + PULONG Key); + +NTSTATUS NTAPI NtSetEaFile( + HANDLE FileHandle, + PIO_STATUS_BLOCK IoStatusBlock, + PVOID Buffer, + ULONG Length); + +NTSTATUS NTAPI NtSetInformationFile( + HANDLE FileHandle, + PIO_STATUS_BLOCK IoStatusBlock, + PVOID FileInformation, + ULONG Length, + FILE_INFORMATION_CLASS FileInformationClass); + +NTSTATUS NTAPI NtSetSecurityObject( + HANDLE Handle, + SECURITY_INFORMATION SecurityInformation, + PSECURITY_DESCRIPTOR SecurityDescriptor); + +NTSTATUS NTAPI NtWriteFile( + HANDLE FileHandle, + HANDLE Event, + PIO_APC_ROUTINE ApcRoutine, + PVOID ApcContext, + PIO_STATUS_BLOCK IoStatusBlock, + PVOID Buffer, + ULONG Length, + PLARGE_INTEGER ByteOffset, + PULONG Key); + +#endif diff --git a/tst/ntptfs/ptfs.sln b/tst/ntptfs/ptfs.sln new file mode 100644 index 00000000..cb0b1525 --- /dev/null +++ b/tst/ntptfs/ptfs.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.25420.1 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ptfs", "ptfs.vcxproj", "{BA69F863-4426-4CAE-8676-331B985C5DA8}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {BA69F863-4426-4CAE-8676-331B985C5DA8}.Debug|x64.ActiveCfg = Debug|x64 + {BA69F863-4426-4CAE-8676-331B985C5DA8}.Debug|x64.Build.0 = Debug|x64 + {BA69F863-4426-4CAE-8676-331B985C5DA8}.Debug|x86.ActiveCfg = Debug|Win32 + {BA69F863-4426-4CAE-8676-331B985C5DA8}.Debug|x86.Build.0 = Debug|Win32 + {BA69F863-4426-4CAE-8676-331B985C5DA8}.Release|x64.ActiveCfg = Release|x64 + {BA69F863-4426-4CAE-8676-331B985C5DA8}.Release|x64.Build.0 = Release|x64 + {BA69F863-4426-4CAE-8676-331B985C5DA8}.Release|x86.ActiveCfg = Release|Win32 + {BA69F863-4426-4CAE-8676-331B985C5DA8}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/tst/ntptfs/ptfs.vcxproj b/tst/ntptfs/ptfs.vcxproj new file mode 100644 index 00000000..d486c6be --- /dev/null +++ b/tst/ntptfs/ptfs.vcxproj @@ -0,0 +1,187 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + + + + + + + + + {BA69F863-4426-4CAE-8676-331B985C5DA8} + Win32Proj + passthrough + $(LatestTargetPlatformVersion) + + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + $(SolutionDir)build\$(Configuration)\ + $(SolutionDir)build\$(ProjectName).build\$(Configuration)\$(PlatformTarget)\ + $(ProjectName)-$(PlatformTarget) + + + true + $(SolutionDir)build\$(Configuration)\ + $(SolutionDir)build\$(ProjectName).build\$(Configuration)\$(PlatformTarget)\ + $(ProjectName)-$(PlatformTarget) + + + false + $(SolutionDir)build\$(Configuration)\ + $(SolutionDir)build\$(ProjectName).build\$(Configuration)\$(PlatformTarget)\ + $(ProjectName)-$(PlatformTarget) + + + false + $(SolutionDir)build\$(Configuration)\ + $(SolutionDir)build\$(ProjectName).build\$(Configuration)\$(PlatformTarget)\ + $(ProjectName)-$(PlatformTarget) + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + $(MSBuildProgramFiles32)\WinFsp\inc + MultiThreaded + + + Console + true + $(MSBuildProgramFiles32)\WinFsp\lib\winfsp-$(PlatformTarget).lib;ntdll.lib;%(AdditionalDependencies) + winfsp-$(PlatformTarget).dll + + + + + + + Level3 + Disabled + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + $(MSBuildProgramFiles32)\WinFsp\inc + MultiThreaded + + + Console + true + $(MSBuildProgramFiles32)\WinFsp\lib\winfsp-$(PlatformTarget).lib;ntdll.lib;%(AdditionalDependencies) + winfsp-$(PlatformTarget).dll + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + $(MSBuildProgramFiles32)\WinFsp\inc + MultiThreaded + + + Console + true + true + true + $(MSBuildProgramFiles32)\WinFsp\lib\winfsp-$(PlatformTarget).lib;ntdll.lib;%(AdditionalDependencies) + winfsp-$(PlatformTarget).dll + + + + + Level3 + + + MaxSpeed + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + $(MSBuildProgramFiles32)\WinFsp\inc + MultiThreaded + + + Console + true + true + true + $(MSBuildProgramFiles32)\WinFsp\lib\winfsp-$(PlatformTarget).lib;ntdll.lib;%(AdditionalDependencies) + winfsp-$(PlatformTarget).dll + + + + + + \ No newline at end of file diff --git a/tst/ntptfs/ptfs.vcxproj.filters b/tst/ntptfs/ptfs.vcxproj.filters new file mode 100644 index 00000000..f512aa21 --- /dev/null +++ b/tst/ntptfs/ptfs.vcxproj.filters @@ -0,0 +1,25 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + + + Source + + + Source + + + Source + + + + + Source + + + \ No newline at end of file diff --git a/tst/winfsp-tests/info-test.c b/tst/winfsp-tests/info-test.c index 04c9a690..9c2cb61e 100644 --- a/tst/winfsp-tests/info-test.c +++ b/tst/winfsp-tests/info-test.c @@ -506,7 +506,7 @@ static void delete_dotest(ULONG Flags, PWSTR Prefix, ULONG FileInfoTimeout) ASSERT(INVALID_HANDLE_VALUE != Handle); CloseHandle(Handle); - Success = RemoveDirectoryW(Dir1Path); + Success = RealRemoveDirectoryW(Dir1Path); ASSERT(!Success); ASSERT(ERROR_DIR_NOT_EMPTY == GetLastError()); diff --git a/tst/winfsp-tests/stream-tests.c b/tst/winfsp-tests/stream-tests.c index 9f8a7d22..4b51dfe4 100644 --- a/tst/winfsp-tests/stream-tests.c +++ b/tst/winfsp-tests/stream-tests.c @@ -1341,7 +1341,7 @@ static void stream_delete_dotest(ULONG Flags, PWSTR Prefix, ULONG FileInfoTimeou ASSERT(INVALID_HANDLE_VALUE != Handle); CloseHandle(Handle); - Success = RemoveDirectoryW(Dir1Path); + Success = RealRemoveDirectoryW(Dir1Path); ASSERT(!Success); ASSERT(ERROR_DIR_NOT_EMPTY == GetLastError()); @@ -1425,7 +1425,7 @@ static void stream_delete_pending_dotest(ULONG Flags, PWSTR Prefix, ULONG FileIn ASSERT(INVALID_HANDLE_VALUE != Handle); CloseHandle(Handle); - Success = RemoveDirectoryW(Dir1Path); + Success = RealRemoveDirectoryW(Dir1Path); ASSERT(!Success); ASSERT(ERROR_DIR_NOT_EMPTY == GetLastError()); diff --git a/tst/winfsp-tests/winfsp-tests.h b/tst/winfsp-tests/winfsp-tests.h index 0ca156eb..759d3595 100644 --- a/tst/winfsp-tests/winfsp-tests.h +++ b/tst/winfsp-tests/winfsp-tests.h @@ -52,6 +52,11 @@ BOOL WINAPI HookDeleteFileW( LPCWSTR lpFileName); BOOL WINAPI HookRemoveDirectoryW( LPCWSTR lpPathName); +static inline BOOL RealRemoveDirectoryW( + LPCWSTR lpPathName) +{ + return RemoveDirectoryW(lpPathName); +} BOOL WINAPI HookMoveFileExW( LPCWSTR lpExistingFileName, LPCWSTR lpNewFileName,