diff --git a/tst/passthrough-dotnet/.gitignore b/tst/passthrough-dotnet/.gitignore new file mode 100644 index 00000000..c78dfb19 --- /dev/null +++ b/tst/passthrough-dotnet/.gitignore @@ -0,0 +1,5 @@ +build +*.ncb +*.suo +*.vcproj.* +*.vcxproj.user diff --git a/tst/passthrough-dotnet/Program.cs b/tst/passthrough-dotnet/Program.cs new file mode 100644 index 00000000..154dde8e --- /dev/null +++ b/tst/passthrough-dotnet/Program.cs @@ -0,0 +1,936 @@ +/** + * @file Program.cs + * + * @copyright 2015-2017 Bill Zissimopoulos + */ +/* + * This file is part of WinFsp. + * + * You can redistribute it and/or modify it under the terms of the GNU + * General Public License version 3 as published by the Free Software + * Foundation. + * + * Licensees holding a valid commercial license may use this file in + * accordance with the commercial license agreement provided with the + * software. + */ + +using System; + +using Fsp; + +namespace passthrough +{ + class PtfsService : Service + { + public PtfsService() : base("PtfsService") + { + } + + protected override void OnStart(String[] Args) + { + } + protected override void OnStop() + { + } + } + + class Program + { + static void Main(string[] args) + { + new PtfsService().Run(); + } + } +} + +#if false + +using namespace Fsp; + +class PTFS : public FileSystem +{ +public: + PTFS(); + ~PTFS(); + NTSTATUS SetPath(PWSTR Path); + +protected: + static NTSTATUS GetFileInfoInternal(HANDLE Handle, FILE_INFO *FileInfo); + NTSTATUS GetVolumeInfo(VOLUME_INFO *VolumeInfo); + NTSTATUS GetSecurityByName( + PWSTR FileName, PUINT32 PFileAttributes/* or ReparsePointIndex */, + PSECURITY_DESCRIPTOR SecurityDescriptor, SIZE_T *PSecurityDescriptorSize); + NTSTATUS Create( + PWSTR FileName, UINT32 CreateOptions, UINT32 GrantedAccess, + UINT32 FileAttributes, PSECURITY_DESCRIPTOR SecurityDescriptor, UINT64 AllocationSize, + FILE_CONTEXT *FileContext, OPEN_FILE_INFO *OpenFileInfo); + NTSTATUS Open( + PWSTR FileName, UINT32 CreateOptions, UINT32 GrantedAccess, + FILE_CONTEXT *FileContext, OPEN_FILE_INFO *OpenFileInfo); + NTSTATUS Overwrite( + const FILE_CONTEXT *FileContext, UINT32 FileAttributes, BOOLEAN ReplaceFileAttributes, UINT64 AllocationSize, + FILE_INFO *FileInfo); + VOID Cleanup( + const FILE_CONTEXT *FileContext, PWSTR FileName, ULONG Flags); + VOID Close( + const FILE_CONTEXT *FileContext); + NTSTATUS Read( + const FILE_CONTEXT *FileContext, PVOID Buffer, UINT64 Offset, ULONG Length, + PULONG PBytesTransferred); + NTSTATUS Write( + const FILE_CONTEXT *FileContext, PVOID Buffer, UINT64 Offset, ULONG Length, + BOOLEAN WriteToEndOfFile, BOOLEAN ConstrainedIo, + PULONG PBytesTransferred, FILE_INFO *FileInfo); + NTSTATUS Flush( + const FILE_CONTEXT *FileContext, + FILE_INFO *FileInfo); + NTSTATUS GetFileInfo( + const FILE_CONTEXT *FileContext, + FILE_INFO *FileInfo); + NTSTATUS SetBasicInfo( + const FILE_CONTEXT *FileContext, UINT32 FileAttributes, + UINT64 CreationTime, UINT64 LastAccessTime, UINT64 LastWriteTime, UINT64 ChangeTime, + FILE_INFO *FileInfo); + NTSTATUS SetFileSize( + const FILE_CONTEXT *FileContext, UINT64 NewSize, BOOLEAN SetAllocationSize, + FILE_INFO *FileInfo); + NTSTATUS CanDelete( + const FILE_CONTEXT *FileContext, PWSTR FileName); + NTSTATUS Rename( + const FILE_CONTEXT *FileContext, + PWSTR FileName, PWSTR NewFileName, BOOLEAN ReplaceIfExists); + NTSTATUS GetSecurity( + const FILE_CONTEXT *FileContext, + PSECURITY_DESCRIPTOR SecurityDescriptor, SIZE_T *PSecurityDescriptorSize); + NTSTATUS SetSecurity( + const FILE_CONTEXT *FileContext, + SECURITY_INFORMATION SecurityInformation, PSECURITY_DESCRIPTOR ModificationDescriptor); + NTSTATUS ReadDirectory( + const FILE_CONTEXT *FileContext, PWSTR Pattern, PWSTR Marker, + PVOID Buffer, ULONG Length, PULONG PBytesTransferred); + +private: + PWSTR _Path; +}; + +struct PTFS_FILE_DESC +{ + PTFS_FILE_DESC() : Handle(INVALID_HANDLE_VALUE), DirBuffer() + { + } + ~PTFS_FILE_DESC() + { + CloseHandle(Handle); + PTFS::DeleteDirectoryBuffer(&DirBuffer); + } + HANDLE Handle; + PVOID DirBuffer; +}; + +PTFS::PTFS() : FileSystem(), _Path() +{ + SetSectorSize(ALLOCATION_UNIT); + SetSectorsPerAllocationUnit(1); + SetFileInfoTimeout(1000); + SetCaseSensitiveSearch(FALSE); + SetCasePreservedNames(TRUE); + SetUnicodeOnDisk(TRUE); + SetPersistentAcls(TRUE); + SetPostCleanupWhenModifiedOnly(TRUE); + SetPassQueryDirectoryPattern(TRUE); +} + +PTFS::~PTFS() +{ + delete[] _Path; +} + +NTSTATUS PTFS::SetPath(PWSTR Path) +{ + WCHAR FullPath[MAX_PATH]; + ULONG Length; + HANDLE Handle; + FILETIME CreationTime; + DWORD LastError; + + Handle = CreateFileW( + Path, FILE_READ_ATTRIBUTES, 0, 0, + OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0); + if (INVALID_HANDLE_VALUE == Handle) + return NtStatusFromWin32(GetLastError()); + + Length = GetFinalPathNameByHandleW(Handle, FullPath, FULLPATH_SIZE - 1, 0); + if (0 == Length) + { + LastError = GetLastError(); + CloseHandle(Handle); + return NtStatusFromWin32(LastError); + } + if (L'\\' == FullPath[Length - 1]) + FullPath[--Length] = L'\0'; + + if (!GetFileTime(Handle, &CreationTime, 0, 0)) + { + LastError = GetLastError(); + CloseHandle(Handle); + return NtStatusFromWin32(LastError); + } + + CloseHandle(Handle); + + Length++; + _Path = new WCHAR[Length]; + memcpy(_Path, FullPath, Length * sizeof(WCHAR)); + + SetVolumeCreationTime(((PLARGE_INTEGER)&CreationTime)->QuadPart); + SetVolumeSerialNumber(0); + + return STATUS_SUCCESS; +} + +NTSTATUS PTFS::GetFileInfoInternal(HANDLE Handle, FILE_INFO *FileInfo) +{ + BY_HANDLE_FILE_INFORMATION ByHandleFileInfo; + + if (!GetFileInformationByHandle(Handle, &ByHandleFileInfo)) + return NtStatusFromWin32(GetLastError()); + + FileInfo->FileAttributes = ByHandleFileInfo.dwFileAttributes; + FileInfo->ReparseTag = 0; + FileInfo->FileSize = + ((UINT64)ByHandleFileInfo.nFileSizeHigh << 32) | (UINT64)ByHandleFileInfo.nFileSizeLow; + FileInfo->AllocationSize = (FileInfo->FileSize + ALLOCATION_UNIT - 1) + / ALLOCATION_UNIT * ALLOCATION_UNIT; + FileInfo->CreationTime = ((PLARGE_INTEGER)&ByHandleFileInfo.ftCreationTime)->QuadPart; + FileInfo->LastAccessTime = ((PLARGE_INTEGER)&ByHandleFileInfo.ftLastAccessTime)->QuadPart; + FileInfo->LastWriteTime = ((PLARGE_INTEGER)&ByHandleFileInfo.ftLastWriteTime)->QuadPart; + FileInfo->ChangeTime = FileInfo->LastWriteTime; + FileInfo->IndexNumber = 0; + FileInfo->HardLinks = 0; + + return STATUS_SUCCESS; +} + +NTSTATUS PTFS::GetVolumeInfo(VOLUME_INFO *VolumeInfo) +{ + WCHAR Root[MAX_PATH]; + ULARGE_INTEGER TotalSize, FreeSize; + + if (!GetVolumePathName(_Path, Root, MAX_PATH)) + return NtStatusFromWin32(GetLastError()); + + if (!GetDiskFreeSpaceEx(Root, 0, &TotalSize, &FreeSize)) + return NtStatusFromWin32(GetLastError()); + + VolumeInfo->TotalSize = TotalSize.QuadPart; + VolumeInfo->FreeSize = FreeSize.QuadPart; + + return STATUS_SUCCESS; +} + +NTSTATUS PTFS::GetSecurityByName( + PWSTR FileName, PUINT32 PFileAttributes/* or ReparsePointIndex */, + PSECURITY_DESCRIPTOR SecurityDescriptor, SIZE_T *PSecurityDescriptorSize) +{ + WCHAR FullPath[FULLPATH_SIZE]; + HANDLE Handle; + FILE_ATTRIBUTE_TAG_INFO AttributeTagInfo; + DWORD SecurityDescriptorSizeNeeded; + NTSTATUS Result; + + if (!ConcatPath(FileName, FullPath)) + return STATUS_OBJECT_NAME_INVALID; + + Handle = CreateFileW(FullPath, + FILE_READ_ATTRIBUTES | READ_CONTROL, 0, 0, + OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0); + if (INVALID_HANDLE_VALUE == Handle) + { + Result = NtStatusFromWin32(GetLastError()); + goto exit; + } + + if (0 != PFileAttributes) + { + if (!GetFileInformationByHandleEx(Handle, + FileAttributeTagInfo, &AttributeTagInfo, sizeof AttributeTagInfo)) + { + Result = NtStatusFromWin32(GetLastError()); + goto exit; + } + + *PFileAttributes = AttributeTagInfo.FileAttributes; + } + + if (0 != PSecurityDescriptorSize) + { + if (!GetKernelObjectSecurity(Handle, + OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION, + SecurityDescriptor, (DWORD)*PSecurityDescriptorSize, &SecurityDescriptorSizeNeeded)) + { + *PSecurityDescriptorSize = SecurityDescriptorSizeNeeded; + Result = NtStatusFromWin32(GetLastError()); + goto exit; + } + + *PSecurityDescriptorSize = SecurityDescriptorSizeNeeded; + } + + Result = STATUS_SUCCESS; + +exit: + if (INVALID_HANDLE_VALUE != Handle) + CloseHandle(Handle); + + return Result; +} + +NTSTATUS PTFS::Create( + PWSTR FileName, UINT32 CreateOptions, UINT32 GrantedAccess, + UINT32 FileAttributes, PSECURITY_DESCRIPTOR SecurityDescriptor, UINT64 AllocationSize, + FILE_CONTEXT *FileContext, OPEN_FILE_INFO *OpenFileInfo) +{ + WCHAR FullPath[FULLPATH_SIZE]; + SECURITY_ATTRIBUTES SecurityAttributes; + ULONG CreateFlags; + PTFS_FILE_DESC *FileDesc; + + if (!ConcatPath(FileName, FullPath)) + return STATUS_OBJECT_NAME_INVALID; + + FileDesc = new PTFS_FILE_DESC; + + SecurityAttributes.nLength = sizeof SecurityAttributes; + SecurityAttributes.lpSecurityDescriptor = SecurityDescriptor; + SecurityAttributes.bInheritHandle = FALSE; + + CreateFlags = FILE_FLAG_BACKUP_SEMANTICS; + if (CreateOptions & FILE_DELETE_ON_CLOSE) + CreateFlags |= FILE_FLAG_DELETE_ON_CLOSE; + + if (CreateOptions & FILE_DIRECTORY_FILE) + { + /* + * It is not widely known but CreateFileW can be used to create directories! + * It requires the specification of both FILE_FLAG_BACKUP_SEMANTICS and + * FILE_FLAG_POSIX_SEMANTICS. It also requires that FileAttributes has + * FILE_ATTRIBUTE_DIRECTORY set. + */ + CreateFlags |= FILE_FLAG_POSIX_SEMANTICS; + FileAttributes |= FILE_ATTRIBUTE_DIRECTORY; + } + else + FileAttributes &= ~FILE_ATTRIBUTE_DIRECTORY; + + if (0 == FileAttributes) + FileAttributes = FILE_ATTRIBUTE_NORMAL; + + FileDesc->Handle = CreateFileW(FullPath, + GrantedAccess, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, &SecurityAttributes, + CREATE_NEW, CreateFlags | FileAttributes, 0); + if (INVALID_HANDLE_VALUE == FileDesc->Handle) + { + delete FileDesc; + return NtStatusFromWin32(GetLastError()); + } + + FileContext->FileDesc = FileDesc; + + return GetFileInfoInternal(FileDesc->Handle, &OpenFileInfo->FileInfo); +} + +NTSTATUS PTFS::Open( + PWSTR FileName, UINT32 CreateOptions, UINT32 GrantedAccess, + FILE_CONTEXT *FileContext, OPEN_FILE_INFO *OpenFileInfo) +{ + WCHAR FullPath[FULLPATH_SIZE]; + ULONG CreateFlags; + PTFS_FILE_DESC *FileDesc; + + if (!ConcatPath(FileName, FullPath)) + return STATUS_OBJECT_NAME_INVALID; + + FileDesc = new PTFS_FILE_DESC; + + CreateFlags = FILE_FLAG_BACKUP_SEMANTICS; + if (CreateOptions & FILE_DELETE_ON_CLOSE) + CreateFlags |= FILE_FLAG_DELETE_ON_CLOSE; + + FileDesc->Handle = CreateFileW(FullPath, + GrantedAccess, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0, + OPEN_EXISTING, CreateFlags, 0); + if (INVALID_HANDLE_VALUE == FileDesc->Handle) + { + delete FileDesc; + return NtStatusFromWin32(GetLastError()); + } + + FileContext->FileDesc = FileDesc; + + return GetFileInfoInternal(FileDesc->Handle, &OpenFileInfo->FileInfo); +} + +NTSTATUS PTFS::Overwrite( + const FILE_CONTEXT *FileContext, UINT32 FileAttributes, BOOLEAN ReplaceFileAttributes, UINT64 AllocationSize, + FILE_INFO *FileInfo) +{ + HANDLE Handle = HandleFromContext(FileContext); + FILE_BASIC_INFO BasicInfo = { 0 }; + FILE_ALLOCATION_INFO AllocationInfo = { 0 }; + FILE_ATTRIBUTE_TAG_INFO AttributeTagInfo; + + if (ReplaceFileAttributes) + { + if (0 == FileAttributes) + FileAttributes = FILE_ATTRIBUTE_NORMAL; + + BasicInfo.FileAttributes = FileAttributes; + if (!SetFileInformationByHandle(Handle, + FileBasicInfo, &BasicInfo, sizeof BasicInfo)) + return NtStatusFromWin32(GetLastError()); + } + else if (0 != FileAttributes) + { + if (!GetFileInformationByHandleEx(Handle, + FileAttributeTagInfo, &AttributeTagInfo, sizeof AttributeTagInfo)) + return NtStatusFromWin32(GetLastError()); + + BasicInfo.FileAttributes = FileAttributes | AttributeTagInfo.FileAttributes; + if (BasicInfo.FileAttributes ^ FileAttributes) + { + if (!SetFileInformationByHandle(Handle, + FileBasicInfo, &BasicInfo, sizeof BasicInfo)) + return NtStatusFromWin32(GetLastError()); + } + } + + if (!SetFileInformationByHandle(Handle, + FileAllocationInfo, &AllocationInfo, sizeof AllocationInfo)) + return NtStatusFromWin32(GetLastError()); + + return GetFileInfoInternal(Handle, FileInfo); +} + +VOID PTFS::Cleanup( + const FILE_CONTEXT *FileContext, PWSTR FileName, ULONG Flags) +{ + HANDLE Handle = HandleFromContext(FileContext); + + if (Flags & CleanupDelete) + { + CloseHandle(Handle); + + /* this will make all future uses of Handle to fail with STATUS_INVALID_HANDLE */ + HandleFromContext(FileContext) = INVALID_HANDLE_VALUE; + } +} + +VOID PTFS::Close( + const FILE_CONTEXT *FileContext) +{ + PTFS_FILE_DESC *FileDesc = (PTFS_FILE_DESC *)FileContext->FileDesc; + + delete FileDesc; +} + +NTSTATUS PTFS::Read( + const FILE_CONTEXT *FileContext, PVOID Buffer, UINT64 Offset, ULONG Length, + PULONG PBytesTransferred) +{ + HANDLE Handle = HandleFromContext(FileContext); + OVERLAPPED Overlapped = { 0 }; + + Overlapped.Offset = (DWORD)Offset; + Overlapped.OffsetHigh = (DWORD)(Offset >> 32); + + if (!ReadFile(Handle, Buffer, Length, PBytesTransferred, &Overlapped)) + return NtStatusFromWin32(GetLastError()); + + return STATUS_SUCCESS; +} + +NTSTATUS PTFS::Write( + const FILE_CONTEXT *FileContext, PVOID Buffer, UINT64 Offset, ULONG Length, + BOOLEAN WriteToEndOfFile, BOOLEAN ConstrainedIo, + PULONG PBytesTransferred, FILE_INFO *FileInfo) +{ + HANDLE Handle = HandleFromContext(FileContext); + LARGE_INTEGER FileSize; + OVERLAPPED Overlapped = { 0 }; + + if (ConstrainedIo) + { + if (!GetFileSizeEx(Handle, &FileSize)) + return NtStatusFromWin32(GetLastError()); + + if (Offset >= (UINT64)FileSize.QuadPart) + return STATUS_SUCCESS; + if (Offset + Length > (UINT64)FileSize.QuadPart) + Length = (ULONG)((UINT64)FileSize.QuadPart - Offset); + } + + Overlapped.Offset = (DWORD)Offset; + Overlapped.OffsetHigh = (DWORD)(Offset >> 32); + + if (!WriteFile(Handle, Buffer, Length, PBytesTransferred, &Overlapped)) + return NtStatusFromWin32(GetLastError()); + + return GetFileInfoInternal(Handle, FileInfo); +} + +NTSTATUS PTFS::Flush( + const FILE_CONTEXT *FileContext, + FILE_INFO *FileInfo) +{ + HANDLE Handle = HandleFromContext(FileContext); + + /* we do not flush the whole volume, so just return SUCCESS */ + if (0 == Handle) + return STATUS_SUCCESS; + + if (!FlushFileBuffers(Handle)) + return NtStatusFromWin32(GetLastError()); + + return GetFileInfoInternal(Handle, FileInfo); +} + +NTSTATUS PTFS::GetFileInfo( + const FILE_CONTEXT *FileContext, + FILE_INFO *FileInfo) +{ + HANDLE Handle = HandleFromContext(FileContext); + + return GetFileInfoInternal(Handle, FileInfo); +} + +NTSTATUS PTFS::SetBasicInfo( + const FILE_CONTEXT *FileContext, UINT32 FileAttributes, + UINT64 CreationTime, UINT64 LastAccessTime, UINT64 LastWriteTime, UINT64 ChangeTime, + FILE_INFO *FileInfo) +{ + HANDLE Handle = HandleFromContext(FileContext); + FILE_BASIC_INFO BasicInfo = { 0 }; + + if (INVALID_FILE_ATTRIBUTES == FileAttributes) + FileAttributes = 0; + else if (0 == FileAttributes) + FileAttributes = FILE_ATTRIBUTE_NORMAL; + + BasicInfo.FileAttributes = FileAttributes; + BasicInfo.CreationTime.QuadPart = CreationTime; + BasicInfo.LastAccessTime.QuadPart = LastAccessTime; + BasicInfo.LastWriteTime.QuadPart = LastWriteTime; + //BasicInfo.ChangeTime = ChangeTime; + + if (!SetFileInformationByHandle(Handle, + FileBasicInfo, &BasicInfo, sizeof BasicInfo)) + return NtStatusFromWin32(GetLastError()); + + return GetFileInfoInternal(Handle, FileInfo); +} + +NTSTATUS PTFS::SetFileSize( + const FILE_CONTEXT *FileContext, UINT64 NewSize, BOOLEAN SetAllocationSize, + FILE_INFO *FileInfo) +{ + HANDLE Handle = HandleFromContext(FileContext); + FILE_ALLOCATION_INFO AllocationInfo; + FILE_END_OF_FILE_INFO EndOfFileInfo; + + if (SetAllocationSize) + { + /* + * This file system does not maintain AllocationSize, although NTFS clearly can. + * However it must always be FileSize <= AllocationSize and NTFS will make sure + * to truncate the FileSize if it sees an AllocationSize < FileSize. + * + * If OTOH a very large AllocationSize is passed, the call below will increase + * the AllocationSize of the underlying file, although our file system does not + * expose this fact. This AllocationSize is only temporary as NTFS will reset + * the AllocationSize of the underlying file when it is closed. + */ + + AllocationInfo.AllocationSize.QuadPart = NewSize; + + if (!SetFileInformationByHandle(Handle, + FileAllocationInfo, &AllocationInfo, sizeof AllocationInfo)) + return NtStatusFromWin32(GetLastError()); + } + else + { + EndOfFileInfo.EndOfFile.QuadPart = NewSize; + + if (!SetFileInformationByHandle(Handle, + FileEndOfFileInfo, &EndOfFileInfo, sizeof EndOfFileInfo)) + return NtStatusFromWin32(GetLastError()); + } + + return GetFileInfoInternal(Handle, FileInfo); +} + +NTSTATUS PTFS::CanDelete( + const FILE_CONTEXT *FileContext, PWSTR FileName) +{ + HANDLE Handle = HandleFromContext(FileContext); + FILE_DISPOSITION_INFO DispositionInfo; + + DispositionInfo.DeleteFile = TRUE; + + if (!SetFileInformationByHandle(Handle, + FileDispositionInfo, &DispositionInfo, sizeof DispositionInfo)) + return NtStatusFromWin32(GetLastError()); + + return STATUS_SUCCESS; +} + +NTSTATUS PTFS::Rename( + const FILE_CONTEXT *FileContext, + PWSTR FileName, PWSTR NewFileName, BOOLEAN ReplaceIfExists) +{ + WCHAR FullPath[FULLPATH_SIZE], NewFullPath[FULLPATH_SIZE]; + + if (!ConcatPath(FileName, FullPath)) + return STATUS_OBJECT_NAME_INVALID; + + if (!ConcatPath(NewFileName, NewFullPath)) + return STATUS_OBJECT_NAME_INVALID; + + if (!MoveFileExW(FullPath, NewFullPath, ReplaceIfExists ? MOVEFILE_REPLACE_EXISTING : 0)) + return NtStatusFromWin32(GetLastError()); + + return STATUS_SUCCESS; +} + +NTSTATUS PTFS::GetSecurity( + const FILE_CONTEXT *FileContext, + PSECURITY_DESCRIPTOR SecurityDescriptor, SIZE_T *PSecurityDescriptorSize) +{ + HANDLE Handle = HandleFromContext(FileContext); + DWORD SecurityDescriptorSizeNeeded; + + if (!GetKernelObjectSecurity(Handle, + OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION, + SecurityDescriptor, (DWORD)*PSecurityDescriptorSize, &SecurityDescriptorSizeNeeded)) + { + *PSecurityDescriptorSize = SecurityDescriptorSizeNeeded; + return NtStatusFromWin32(GetLastError()); + } + + *PSecurityDescriptorSize = SecurityDescriptorSizeNeeded; + + return STATUS_SUCCESS; +} + +NTSTATUS PTFS::SetSecurity( + const FILE_CONTEXT *FileContext, + SECURITY_INFORMATION SecurityInformation, PSECURITY_DESCRIPTOR ModificationDescriptor) +{ + HANDLE Handle = HandleFromContext(FileContext); + + if (!SetKernelObjectSecurity(Handle, SecurityInformation, ModificationDescriptor)) + return NtStatusFromWin32(GetLastError()); + + return STATUS_SUCCESS; +} + +NTSTATUS PTFS::ReadDirectory( + const FILE_CONTEXT *FileContext, PWSTR Pattern, PWSTR Marker, + PVOID Buffer, ULONG BufferLength, PULONG PBytesTransferred) +{ + PTFS_FILE_DESC *FileDesc = (PTFS_FILE_DESC *)FileContext->FileDesc; + HANDLE Handle = FileDesc->Handle; + WCHAR FullPath[FULLPATH_SIZE]; + ULONG Length, PatternLength; + HANDLE FindHandle; + WIN32_FIND_DATAW FindData; + union + { + UINT8 B[FIELD_OFFSET(FSP_FSCTL_DIR_INFO, FileNameBuf) + MAX_PATH * sizeof(WCHAR)]; + FSP_FSCTL_DIR_INFO D; + } DirInfoBuf; + FSP_FSCTL_DIR_INFO *DirInfo = &DirInfoBuf.D; + NTSTATUS DirBufferResult; + + DirBufferResult = STATUS_SUCCESS; + if (AcquireDirectoryBuffer(&FileDesc->DirBuffer, 0 == Marker, &DirBufferResult)) + { + if (0 == Pattern) + Pattern = L"*"; + PatternLength = (ULONG)wcslen(Pattern); + + Length = GetFinalPathNameByHandleW(Handle, FullPath, FULLPATH_SIZE - 1, 0); + if (0 == Length) + DirBufferResult = NtStatusFromWin32(GetLastError()); + else if (Length + 1 + PatternLength >= FULLPATH_SIZE) + DirBufferResult = STATUS_OBJECT_NAME_INVALID; + if (!NT_SUCCESS(DirBufferResult)) + { + ReleaseDirectoryBuffer(&FileDesc->DirBuffer); + return DirBufferResult; + } + + if (L'\\' != FullPath[Length - 1]) + FullPath[Length++] = L'\\'; + memcpy(FullPath + Length, Pattern, PatternLength * sizeof(WCHAR)); + FullPath[Length + PatternLength] = L'\0'; + + FindHandle = FindFirstFileW(FullPath, &FindData); + if (INVALID_HANDLE_VALUE != FindHandle) + { + do + { + memset(DirInfo, 0, sizeof *DirInfo); + Length = (ULONG)wcslen(FindData.cFileName); + DirInfo->Size = (UINT16)(FIELD_OFFSET(FSP_FSCTL_DIR_INFO, FileNameBuf) + Length * sizeof(WCHAR)); + DirInfo->FileInfo.FileAttributes = FindData.dwFileAttributes; + DirInfo->FileInfo.ReparseTag = 0; + DirInfo->FileInfo.FileSize = + ((UINT64)FindData.nFileSizeHigh << 32) | (UINT64)FindData.nFileSizeLow; + DirInfo->FileInfo.AllocationSize = (DirInfo->FileInfo.FileSize + ALLOCATION_UNIT - 1) + / ALLOCATION_UNIT * ALLOCATION_UNIT; + DirInfo->FileInfo.CreationTime = ((PLARGE_INTEGER)&FindData.ftCreationTime)->QuadPart; + DirInfo->FileInfo.LastAccessTime = ((PLARGE_INTEGER)&FindData.ftLastAccessTime)->QuadPart; + DirInfo->FileInfo.LastWriteTime = ((PLARGE_INTEGER)&FindData.ftLastWriteTime)->QuadPart; + DirInfo->FileInfo.ChangeTime = DirInfo->FileInfo.LastWriteTime; + DirInfo->FileInfo.IndexNumber = 0; + DirInfo->FileInfo.HardLinks = 0; + memcpy(DirInfo->FileNameBuf, FindData.cFileName, Length * sizeof(WCHAR)); + + if (!FillDirectoryBuffer(&FileDesc->DirBuffer, DirInfo, &DirBufferResult)) + break; + } while (FindNextFileW(FindHandle, &FindData)); + + FindClose(FindHandle); + } + + ReleaseDirectoryBuffer(&FileDesc->DirBuffer); + } + + if (!NT_SUCCESS(DirBufferResult)) + return DirBufferResult; + + ReadDirectoryBuffer(&FileDesc->DirBuffer, + Marker, Buffer, BufferLength, PBytesTransferred); + + return STATUS_SUCCESS; +} + +class PTFS_SERVICE : public Service +{ +public: + PTFS_SERVICE(); + +protected: + NTSTATUS OnStart(ULONG Argc, PWSTR *Argv); + NTSTATUS OnStop(); + +private: + PTFS *_Ptfs; +}; + +static NTSTATUS EnableBackupRestorePrivileges(VOID) +{ + union + { + TOKEN_PRIVILEGES P; + UINT8 B[sizeof(TOKEN_PRIVILEGES) + sizeof(LUID_AND_ATTRIBUTES)]; + } Privileges; + HANDLE Token; + + Privileges.P.PrivilegeCount = 2; + Privileges.P.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; + Privileges.P.Privileges[1].Attributes = SE_PRIVILEGE_ENABLED; + + if (!LookupPrivilegeValueW(0, SE_BACKUP_NAME, &Privileges.P.Privileges[0].Luid) || + !LookupPrivilegeValueW(0, SE_RESTORE_NAME, &Privileges.P.Privileges[1].Luid)) + return NtStatusFromWin32(GetLastError()); + + if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &Token)) + return NtStatusFromWin32(GetLastError()); + + if (!AdjustTokenPrivileges(Token, FALSE, &Privileges.P, 0, 0, 0)) + { + CloseHandle(Token); + + return NtStatusFromWin32(GetLastError()); + } + + CloseHandle(Token); + + return STATUS_SUCCESS; +} + +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; +} + +PTFS_SERVICE::PTFS_SERVICE() : Service(L"" PROGNAME), _Ptfs(0) +{ +} + +NTSTATUS PTFS_SERVICE::OnStart(ULONG argc, PWSTR *argv) +{ + + wchar_t **argp, **arge; + PWSTR DebugLogFile = 0; + ULONG DebugFlags = 0; + PWSTR VolumePrefix = 0; + PWSTR PassThrough = 0; + PWSTR MountPoint = 0; + HANDLE DebugLogHandle = INVALID_HANDLE_VALUE; + WCHAR PassThroughBuf[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'p': + argtos(PassThrough); + break; + case L'u': + argtos(VolumePrefix); + break; + default: + goto usage; + } + } + + if (arge > argp) + goto usage; + + if (0 == PassThrough && 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(PassThroughBuf, sizeof PassThroughBuf, L"%c:%s", P[1], P + 3); + PassThrough = PassThroughBuf; + } + } + } + + if (0 == PassThrough || 0 == MountPoint) + goto usage; + + EnableBackupRestorePrivileges(); + + 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); + } + + Ptfs = new PTFS; + + Ptfs->SetPrefix(VolumePrefix); + + Result = Ptfs->SetPath(PassThrough); + if (!NT_SUCCESS(Result)) + { + fail(L"cannot create file system"); + goto exit; + } + + Result = Ptfs->Mount(MountPoint, 0, FALSE, DebugFlags); + if (!NT_SUCCESS(Result)) + { + fail(L"cannot mount file system"); + goto exit; + } + + MountPoint = Ptfs->MountPoint(); + + info(L"%s%s%s -p %s -m %s", + L"" PROGNAME, + 0 != VolumePrefix && L'\0' != VolumePrefix[0] ? L" -u " : L"", + 0 != VolumePrefix && L'\0' != VolumePrefix[0] ? VolumePrefix : L"", + PassThrough, + MountPoint); + + _Ptfs = Ptfs; + Result = STATUS_SUCCESS; + +exit: + if (!NT_SUCCESS(Result) && 0 != Ptfs) + delete 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" + " -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 +} + +NTSTATUS PTFS_SERVICE::OnStop() +{ + _Ptfs->Unmount(); + delete _Ptfs; + _Ptfs = 0; + + return STATUS_SUCCESS; +} + +int wmain(int argc, wchar_t **argv) +{ + return PTFS_SERVICE().Run(); +} +#endif diff --git a/tst/passthrough-dotnet/passthrough-dotnet.csproj b/tst/passthrough-dotnet/passthrough-dotnet.csproj new file mode 100644 index 00000000..739ee402 --- /dev/null +++ b/tst/passthrough-dotnet/passthrough-dotnet.csproj @@ -0,0 +1,83 @@ + + + + + Debug + AnyCPU + {6EC13EBC-BD7E-4997-9B29-49D5F06103A6} + Exe + passthrough + passthrough-dotnet + v4.5.2 + 512 + true + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + false + true + + + AnyCPU + true + full + false + $(SolutionDir)build\$(Configuration)\ + $(SolutionDir)build\$(ProjectName).build\ + $(BaseIntermediateOutputPath)$(Configuration)\ + DEBUG;TRACE + prompt + 4 + false + + + AnyCPU + pdbonly + true + $(SolutionDir)build\$(Configuration)\ + $(SolutionDir)build\$(ProjectName).build\ + $(BaseIntermediateOutputPath)$(Configuration)\ + TRACE + prompt + 4 + + + + + ..\..\build\VStudio\build\Debug\winfsp-msil.dll + + + + + + + + False + Microsoft .NET Framework 4.5.2 %28x86 and x64%29 + true + + + False + .NET Framework 3.5 SP1 + false + + + + + \ No newline at end of file diff --git a/tst/passthrough-dotnet/passthrough-dotnet.sln b/tst/passthrough-dotnet/passthrough-dotnet.sln new file mode 100644 index 00000000..8fbde580 --- /dev/null +++ b/tst/passthrough-dotnet/passthrough-dotnet.sln @@ -0,0 +1,22 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.25420.1 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "passthrough-dotnet", "passthrough-dotnet.csproj", "{6EC13EBC-BD7E-4997-9B29-49D5F06103A6}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {6EC13EBC-BD7E-4997-9B29-49D5F06103A6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6EC13EBC-BD7E-4997-9B29-49D5F06103A6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6EC13EBC-BD7E-4997-9B29-49D5F06103A6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6EC13EBC-BD7E-4997-9B29-49D5F06103A6}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal