From 83c9351d387f8cea3f48106481edf01acb1085c1 Mon Sep 17 00:00:00 2001 From: Bill Zissimopoulos Date: Fri, 7 Apr 2017 22:28:57 -0700 Subject: [PATCH] tst: passthrough-dotnet: WIP --- tst/passthrough-dotnet/Program.cs | 1193 ++++++++++++++--------------- 1 file changed, 590 insertions(+), 603 deletions(-) diff --git a/tst/passthrough-dotnet/Program.cs b/tst/passthrough-dotnet/Program.cs index 25e5d0ae..e4873aeb 100644 --- a/tst/passthrough-dotnet/Program.cs +++ b/tst/passthrough-dotnet/Program.cs @@ -16,11 +16,455 @@ */ using System; +using System.IO; +using System.Runtime.InteropServices; +using System.Security.AccessControl; using Fsp; +using VolumeInfo = Fsp.Interop.VolumeInfo; +using FileInfo = Fsp.Interop.FileInfo; namespace passthrough { + class Ptfs : FileSystem + { + private const int ALLOCATION_UNIT = 4096; + + protected class FileDesc + { + public FileSystemInfo Info; + public FileStream Stream; + + public FileDesc(FileSystemInfo Info, FileStream Stream) + { + this.Info = Info; + this.Stream = Stream; + } + public UInt32 FileAttributes + { + get + { + return (UInt32)Info.Attributes; + } + set + { + Info.Attributes = 0 == value ? + System.IO.FileAttributes.Normal : (FileAttributes)value; + } + } + } + + public Ptfs() : base() + { + SetSectorSize(ALLOCATION_UNIT); + SetSectorsPerAllocationUnit(1); + SetFileInfoTimeout(1000); + SetCaseSensitiveSearch(false); + SetCasePreservedNames(true); + SetUnicodeOnDisk(true); + SetPersistentAcls(true); + SetPostCleanupWhenModifiedOnly(true); + SetPassQueryDirectoryPattern(true); + } + void SetPath(String value) + { + _Path = Path.GetFullPath(value); + SetVolumeCreationTime((UInt64)File.GetCreationTimeUtc(_Path).ToFileTimeUtc()); + SetVolumeSerialNumber(0); + } + + protected override Int32 ExceptionHandler(Exception ex) + { + Int32 HResult = ex.HResult; /* needs Framework 4.5 */ + if (0x80070000 == (HResult & 0xFFFF0000)) + return NtStatusFromWin32((UInt32)HResult & 0xFFFF); + return STATUS_UNEXPECTED_IO_ERROR; + } + protected String ConcatPath(String FileName) + { + return Path.Combine(_Path, FileName); + } + protected Int32 GetFileInfoInternal(FileDesc FileDesc, Boolean Refresh, + out FileInfo FileInfo) + { + if (Refresh) + FileDesc.Info.Refresh(); + FileInfo.FileAttributes = FileDesc.FileAttributes; + FileInfo.ReparseTag = 0; + FileInfo.FileSize = FileDesc.Info is System.IO.FileInfo ? + (UInt64)((System.IO.FileInfo)FileDesc.Info).Length : 0; + FileInfo.AllocationSize = (FileInfo.FileSize + ALLOCATION_UNIT - 1) + / ALLOCATION_UNIT * ALLOCATION_UNIT; + FileInfo.CreationTime = (UInt64)FileDesc.Info.CreationTimeUtc.ToFileTimeUtc(); + FileInfo.LastAccessTime = (UInt64)FileDesc.Info.LastAccessTimeUtc.ToFileTimeUtc(); + FileInfo.LastWriteTime = (UInt64)FileDesc.Info.LastWriteTimeUtc.ToFileTimeUtc(); + FileInfo.ChangeTime = FileInfo.LastWriteTime; + FileInfo.IndexNumber = 0; + FileInfo.HardLinks = 0; + return STATUS_SUCCESS; + } + protected override Int32 GetVolumeInfo( + out VolumeInfo VolumeInfo) + { + VolumeInfo = default(VolumeInfo); + try + { + DriveInfo Info = new DriveInfo(_Path); + VolumeInfo.TotalSize = (UInt64)Info.TotalSize; + VolumeInfo.FreeSize = (UInt64)Info.TotalFreeSpace; + } + catch (ArgumentException) + { + /* + * DriveInfo only supports drives and does not support UNC paths. + * It would be better to use GetDiskFreeSpaceEx here. + */ + } + return STATUS_SUCCESS; + } + protected override Int32 GetSecurityByName( + String FileName, + out UInt32 FileAttributes/* or ReparsePointIndex */, + ref Byte[] SecurityDescriptor) + { + FileName = ConcatPath(FileName); + System.IO.FileInfo Info = new System.IO.FileInfo(FileName); + FileAttributes = (UInt32)Info.Attributes; + if (null != SecurityDescriptor) + SecurityDescriptor = Info.GetAccessControl().GetSecurityDescriptorBinaryForm(); + return STATUS_SUCCESS; + } + protected override Int32 Create( + String FileName, + UInt32 CreateOptions, + UInt32 GrantedAccess, + UInt32 FileAttributes, + Byte[] SecurityDescriptor, + UInt64 AllocationSize, + out Object FileNode, + out Object FileDesc0, + out FileInfo FileInfo, + out String NormalizedName) + { + FileDesc FileDesc; + FileName = ConcatPath(FileName); + if (0 != (CreateOptions & FILE_DIRECTORY_FILE)) + { + DirectorySecurity Security = null; + if (null != SecurityDescriptor) + { + Security = new DirectorySecurity(); + Security.SetSecurityDescriptorBinaryForm(SecurityDescriptor); + } + // ???: FILE_DELETE_ON_CLOSE + FileDesc = new FileDesc( + Directory.CreateDirectory(FileName, Security), + null); + } + else + { + FileSecurity Security = null; + if (null != SecurityDescriptor) + { + Security = new FileSecurity(); + Security.SetSecurityDescriptorBinaryForm(SecurityDescriptor); + } + FileOptions Options = 0 != (CreateOptions & FILE_DELETE_ON_CLOSE) ? + FileOptions.DeleteOnClose : 0; + FileDesc = new FileDesc( + new System.IO.FileInfo(FileName), + new FileStream( + FileName, + FileMode.CreateNew, + (FileSystemRights)GrantedAccess, + FileShare.Read | FileShare.Write | FileShare.Delete, + 4096, + Options, + Security)); + } + FileDesc.FileAttributes = FileAttributes; + FileNode = default(Object); + FileDesc0 = FileDesc; + NormalizedName = default(String); + return GetFileInfoInternal(FileDesc, false, out FileInfo); + } + protected override Int32 Open( + String FileName, + UInt32 CreateOptions, + UInt32 GrantedAccess, + out Object FileNode, + out Object FileDesc0, + out FileInfo FileInfo, + out String NormalizedName) + { + FileDesc FileDesc; + FileName = ConcatPath(FileName); + if (Directory.Exists(FileName)) + { + // ???: FILE_DELETE_ON_CLOSE + FileDesc = new FileDesc( + new System.IO.DirectoryInfo(FileName), + null); + } + else + { + FileOptions Options = 0 != (CreateOptions & FILE_DELETE_ON_CLOSE) ? + FileOptions.DeleteOnClose : 0; + FileDesc = new FileDesc( + new System.IO.FileInfo(FileName), + new FileStream( + FileName, + FileMode.Open, + (FileSystemRights)GrantedAccess, + FileShare.Read | FileShare.Write | FileShare.Delete, + 4096, + Options)); + } + FileNode = default(Object); + FileDesc0 = FileDesc; + NormalizedName = default(String); + return GetFileInfoInternal(FileDesc, false, out FileInfo); + } + protected override Int32 Overwrite( + Object FileNode, + Object FileDesc0, + UInt32 FileAttributes, + Boolean ReplaceFileAttributes, + UInt64 AllocationSize, + out FileInfo FileInfo) + { + FileDesc FileDesc = (FileDesc)FileDesc0; + if (ReplaceFileAttributes) + FileDesc.FileAttributes = FileAttributes; + else if (0 != FileAttributes) + FileDesc.FileAttributes |= FileAttributes; + FileDesc.Stream.SetLength(0); + return GetFileInfoInternal(FileDesc, true, out FileInfo); + } + protected override void Cleanup( + Object FileNode, + Object FileDesc0, + String FileName, + UInt32 Flags) + { + FileDesc FileDesc = (FileDesc)FileDesc0; + if (0 == (Flags & CleanupDelete)) + FileDesc.Stream.Dispose(); + } + protected override void Close( + Object FileNode, + Object FileDesc0) + { + FileDesc FileDesc = (FileDesc)FileDesc0; + FileDesc.Stream.Dispose(); + } + protected override Int32 Read( + Object FileNode, + Object FileDesc0, + IntPtr Buffer, + UInt64 Offset, + UInt32 Length, + out UInt32 PBytesTransferred) + { + FileDesc FileDesc = (FileDesc)FileDesc0; + Byte[] Bytes = new byte[Length]; + FileDesc.Stream.Seek((Int64)Offset, SeekOrigin.Begin); + PBytesTransferred = (UInt32)FileDesc.Stream.Read(Bytes, 0, Bytes.Length); + Marshal.Copy(Bytes, 0, Buffer, Bytes.Length); + return STATUS_SUCCESS; + } + protected override Int32 Write( + Object FileNode, + Object FileDesc0, + IntPtr Buffer, + UInt64 Offset, + UInt32 Length, + Boolean WriteToEndOfFile, + Boolean ConstrainedIo, + out UInt32 PBytesTransferred, + out FileInfo FileInfo) + { + FileDesc FileDesc = (FileDesc)FileDesc0; + UInt64 FileSize; + if (ConstrainedIo) + { + FileDesc.Info.Refresh(); + FileSize = (UInt64)((System.IO.FileInfo)FileDesc.Info).Length; + if (Offset >= FileSize) + { + PBytesTransferred = default(UInt32); + FileInfo = default(FileInfo); + return STATUS_SUCCESS; + } + if (Offset + Length > FileSize) + Length = (UInt32)(FileSize - Offset); + } + Byte[] Bytes = new byte[Length]; + Marshal.Copy(Buffer, Bytes, 0, Bytes.Length); + FileDesc.Stream.Seek((Int64)Offset, SeekOrigin.Begin); + FileDesc.Stream.Write(Bytes, 0, Bytes.Length); + PBytesTransferred = (UInt32)Bytes.Length; + return GetFileInfoInternal(FileDesc, true, out FileInfo); + } + protected override Int32 Flush( + Object FileNode, + Object FileDesc0, + out FileInfo FileInfo) + { + FileDesc FileDesc = (FileDesc)FileDesc0; + if (null == FileDesc) + { + /* we do not flush the whole volume, so just return SUCCESS */ + FileInfo = default(FileInfo); + return STATUS_SUCCESS; + } + FileDesc.Stream.Flush(true); + return GetFileInfoInternal(FileDesc, true, out FileInfo); + } + protected override Int32 GetFileInfo( + Object FileNode, + Object FileDesc0, + out FileInfo FileInfo) + { + FileDesc FileDesc = (FileDesc)FileDesc0; + return GetFileInfoInternal(FileDesc, true, out FileInfo); + } + protected override Int32 SetBasicInfo( + Object FileNode, + Object FileDesc0, + UInt32 FileAttributes, + UInt64 CreationTime, + UInt64 LastAccessTime, + UInt64 LastWriteTime, + UInt64 ChangeTime, + out FileInfo FileInfo) + { + FileDesc FileDesc = (FileDesc)FileDesc0; + if (unchecked((UInt32)(-1)) != FileAttributes) + FileDesc.FileAttributes = FileAttributes; + if (0 != CreationTime) + FileDesc.Info.CreationTimeUtc = DateTime.FromFileTimeUtc((Int64)CreationTime); + if (0 != LastAccessTime) + FileDesc.Info.LastAccessTimeUtc = DateTime.FromFileTimeUtc((Int64)LastAccessTime); + if (0 != LastWriteTime) + FileDesc.Info.LastWriteTimeUtc = DateTime.FromFileTimeUtc((Int64)LastWriteTime); + return GetFileInfoInternal(FileDesc, true, out FileInfo); + } + protected override Int32 SetFileSize( + Object FileNode, + Object FileDesc0, + UInt64 NewSize, + Boolean SetAllocationSize, + out FileInfo FileInfo) + { + FileDesc FileDesc = (FileDesc)FileDesc0; + GetFileInfoInternal(FileDesc, true, out FileInfo); + if (!SetAllocationSize || FileInfo.FileSize > NewSize) + { + /* + * "FileInfo.FileSize > NewSize" explanation: + * Ptfs does not support allocation size. However if the new AllocationSize + * is less than the current FileSize we must truncate the file. + */ + FileDesc.Stream.SetLength((Int64)NewSize); + FileInfo.FileSize = NewSize; + FileInfo.AllocationSize = (FileInfo.FileSize + ALLOCATION_UNIT - 1) + / ALLOCATION_UNIT * ALLOCATION_UNIT; + } + return STATUS_SUCCESS; + } + protected override Int32 CanDelete( + Object FileNode, + Object FileDesc0, + String FileName) + { + FileDesc FileDesc = (FileDesc)FileDesc0; + FileName = ConcatPath(FileName); + /* + * If a file has an open handle the Delete call below + * will only mark it for disposition. + */ + if (null == FileDesc.Stream) + Directory.Delete(FileName); + else + File.Delete(FileName); + return STATUS_SUCCESS; + } + protected override Int32 Rename( + Object FileNode, + Object FileDesc0, + String FileName, + String NewFileName, + Boolean ReplaceIfExists) + { + FileDesc FileDesc = (FileDesc)FileDesc0; + FileName = ConcatPath(FileName); + NewFileName = ConcatPath(NewFileName); + if (null == FileDesc.Stream) + { + if (ReplaceIfExists) + return STATUS_ACCESS_DENIED; + Directory.Move(FileName, NewFileName); + } + else + { + if (ReplaceIfExists) + File.Delete(NewFileName); + File.Move(FileName, NewFileName); + } + return STATUS_SUCCESS; + } + protected override Int32 GetSecurity( + Object FileNode, + Object FileDesc0, + ref Byte[] SecurityDescriptor) + { + FileDesc FileDesc = (FileDesc)FileDesc0; + if (null == FileDesc.Stream) + SecurityDescriptor = ((System.IO.DirectoryInfo)FileDesc.Info).GetAccessControl(). + GetSecurityDescriptorBinaryForm(); + else + SecurityDescriptor = ((System.IO.FileInfo)FileDesc.Info).GetAccessControl(). + GetSecurityDescriptorBinaryForm(); + return STATUS_SUCCESS; + } + protected override Int32 SetSecurity( + Object FileNode, + Object FileDesc0, + AccessControlSections Sections, + Byte[] SecurityDescriptor) + { + FileDesc FileDesc = (FileDesc)FileDesc0; + if (null == FileDesc.Stream) + { + DirectorySecurity Security = ((System.IO.DirectoryInfo)FileDesc.Info).GetAccessControl(); + Security.SetSecurityDescriptorBinaryForm(SecurityDescriptor, Sections); + ((System.IO.DirectoryInfo)FileDesc.Info).SetAccessControl(Security); + } + else + { + FileSecurity Security = ((System.IO.FileInfo)FileDesc.Info).GetAccessControl(); + Security.SetSecurityDescriptorBinaryForm(SecurityDescriptor, Sections); + ((System.IO.FileInfo)FileDesc.Info).SetAccessControl(Security); + } + return STATUS_SUCCESS; + } + protected override Int32 ReadDirectory( + Object FileNode, + Object FileDesc, + String Pattern, + String Marker, + IntPtr Buffer, + UInt32 Length, + out UInt32 PBytesTransferred) + { + PBytesTransferred = default(UInt32); + return STATUS_INVALID_DEVICE_REQUEST; + } + + private String _Path; + } + class PtfsService : Service { public PtfsService() : base("PtfsService") @@ -29,10 +473,156 @@ namespace passthrough protected override void OnStart(String[] Args) { +#if false + wchar_t **argp, **arge; + String DebugLogFile = null; + UInt32 DebugFlags = 0; + String VolumePrefix = null; + String PassThrough = null; + String MountPoint = null; + 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; +#endif } protected override void OnStop() { + _Ptfs.Unmount(); + _Ptfs = null; } + + private Ptfs _Ptfs; } class Program @@ -48,72 +638,6 @@ namespace passthrough 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() @@ -128,512 +652,6 @@ struct PTFS_FILE_DESC 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) @@ -716,19 +734,6 @@ NTSTATUS PTFS::ReadDirectory( 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 @@ -768,10 +773,6 @@ static ULONG wcstol_deflt(wchar_t *w, ULONG deflt) 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) { @@ -919,18 +920,4 @@ usage: #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