From 2ee3f02928594a164a360ee92d4fbbec3be5b019 Mon Sep 17 00:00:00 2001 From: Bill Zissimopoulos Date: Thu, 4 May 2017 21:56:46 -0700 Subject: [PATCH] tst: memfs-dotnet: WIP --- build/VStudio/testing/memfs-dotnet.csproj | 62 ++ build/VStudio/winfsp.sln | 27 + tst/memfs-dotnet/Program.cs | 1240 +++++++++++++++++++++ 3 files changed, 1329 insertions(+) create mode 100644 build/VStudio/testing/memfs-dotnet.csproj create mode 100644 tst/memfs-dotnet/Program.cs diff --git a/build/VStudio/testing/memfs-dotnet.csproj b/build/VStudio/testing/memfs-dotnet.csproj new file mode 100644 index 00000000..ee0f3205 --- /dev/null +++ b/build/VStudio/testing/memfs-dotnet.csproj @@ -0,0 +1,62 @@ + + + + + Debug + AnyCPU + {4920E350-D496-4652-AE98-6C4208AEC1D8} + Exe + Properties + memfs + memfs-dotnet-msil + v4.5.2 + 512 + 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 + + + + + + + Program.cs + + + + + {94580219-cc8d-4fe5-a3be-437b0b3481e1} + winfsp.net + + + + + \ No newline at end of file diff --git a/build/VStudio/winfsp.sln b/build/VStudio/winfsp.sln index 3cb6ccd7..f3dcb729 100644 --- a/build/VStudio/winfsp.sln +++ b/build/VStudio/winfsp.sln @@ -61,6 +61,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "winfsp.net", "dotnet\winfsp EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "dotnet", "dotnet", "{A998CEC4-4B34-43DC-8457-F7761228BA67}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "memfs-dotnet", "testing\memfs-dotnet.csproj", "{4920E350-D496-4652-AE98-6C4208AEC1D8}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -259,6 +261,30 @@ Global {94580219-CC8D-4FE5-A3BE-437B0B3481E1}.Release|x64.Build.0 = Release|Any CPU {94580219-CC8D-4FE5-A3BE-437B0B3481E1}.Release|x86.ActiveCfg = Release|Any CPU {94580219-CC8D-4FE5-A3BE-437B0B3481E1}.Release|x86.Build.0 = Release|Any CPU + {4920E350-D496-4652-AE98-6C4208AEC1D8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4920E350-D496-4652-AE98-6C4208AEC1D8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4920E350-D496-4652-AE98-6C4208AEC1D8}.Debug|x64.ActiveCfg = Debug|Any CPU + {4920E350-D496-4652-AE98-6C4208AEC1D8}.Debug|x64.Build.0 = Debug|Any CPU + {4920E350-D496-4652-AE98-6C4208AEC1D8}.Debug|x86.ActiveCfg = Debug|Any CPU + {4920E350-D496-4652-AE98-6C4208AEC1D8}.Debug|x86.Build.0 = Debug|Any CPU + {4920E350-D496-4652-AE98-6C4208AEC1D8}.Installer.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4920E350-D496-4652-AE98-6C4208AEC1D8}.Installer.Debug|Any CPU.Build.0 = Debug|Any CPU + {4920E350-D496-4652-AE98-6C4208AEC1D8}.Installer.Debug|x64.ActiveCfg = Debug|Any CPU + {4920E350-D496-4652-AE98-6C4208AEC1D8}.Installer.Debug|x64.Build.0 = Debug|Any CPU + {4920E350-D496-4652-AE98-6C4208AEC1D8}.Installer.Debug|x86.ActiveCfg = Debug|Any CPU + {4920E350-D496-4652-AE98-6C4208AEC1D8}.Installer.Debug|x86.Build.0 = Debug|Any CPU + {4920E350-D496-4652-AE98-6C4208AEC1D8}.Installer.Release|Any CPU.ActiveCfg = Release|Any CPU + {4920E350-D496-4652-AE98-6C4208AEC1D8}.Installer.Release|Any CPU.Build.0 = Release|Any CPU + {4920E350-D496-4652-AE98-6C4208AEC1D8}.Installer.Release|x64.ActiveCfg = Release|Any CPU + {4920E350-D496-4652-AE98-6C4208AEC1D8}.Installer.Release|x64.Build.0 = Release|Any CPU + {4920E350-D496-4652-AE98-6C4208AEC1D8}.Installer.Release|x86.ActiveCfg = Release|Any CPU + {4920E350-D496-4652-AE98-6C4208AEC1D8}.Installer.Release|x86.Build.0 = Release|Any CPU + {4920E350-D496-4652-AE98-6C4208AEC1D8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4920E350-D496-4652-AE98-6C4208AEC1D8}.Release|Any CPU.Build.0 = Release|Any CPU + {4920E350-D496-4652-AE98-6C4208AEC1D8}.Release|x64.ActiveCfg = Release|Any CPU + {4920E350-D496-4652-AE98-6C4208AEC1D8}.Release|x64.Build.0 = Release|Any CPU + {4920E350-D496-4652-AE98-6C4208AEC1D8}.Release|x86.ActiveCfg = Release|Any CPU + {4920E350-D496-4652-AE98-6C4208AEC1D8}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -273,5 +299,6 @@ Global {10757011-749D-4954-873B-AE38D8145472} = {69439FD1-C07D-4BF1-98DC-3CCFECE53A49} {C4E1E9E5-0959-488E-8C6A-C327CC81BEFB} = {69439FD1-C07D-4BF1-98DC-3CCFECE53A49} {94580219-CC8D-4FE5-A3BE-437B0B3481E1} = {A998CEC4-4B34-43DC-8457-F7761228BA67} + {4920E350-D496-4652-AE98-6C4208AEC1D8} = {69439FD1-C07D-4BF1-98DC-3CCFECE53A49} EndGlobalSection EndGlobal diff --git a/tst/memfs-dotnet/Program.cs b/tst/memfs-dotnet/Program.cs new file mode 100644 index 00000000..c4957123 --- /dev/null +++ b/tst/memfs-dotnet/Program.cs @@ -0,0 +1,1240 @@ +/** + * @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 System.Collections.Generic; +using System.IO; +using System.Security.AccessControl; + +using Fsp; +using VolumeInfo = Fsp.Interop.VolumeInfo; +using FileInfo = Fsp.Interop.FileInfo; + +namespace memfs +{ + class FileNode + { + public FileNode(String FileName) + { + this.FileName = FileName; + FileInfo.CreationTime = + FileInfo.LastAccessTime = + FileInfo.LastWriteTime = + FileInfo.ChangeTime = (UInt64)DateTime.Now.ToFileTimeUtc(); + FileInfo.IndexNumber = IndexNumber++; + } + public FileInfo GetFileInfo() + { + if (null == MainFileNode) + return this.FileInfo; + else + { + FileInfo FileInfo = MainFileNode.FileInfo; + FileInfo.FileAttributes &= ~(UInt32)System.IO.FileAttributes.Directory; + /* named streams cannot be directories */ + FileInfo.AllocationSize = this.FileInfo.AllocationSize; + FileInfo.FileSize = this.FileInfo.FileSize; + return FileInfo; + } + } + + private static UInt64 IndexNumber = 1; + public String FileName; + public FileInfo FileInfo; + public Byte[] FileSecurity; + public Byte[] FileData; + public Byte[] ReparseData; + public FileNode MainFileNode; + } + + class FileNodeMap + { + public FileNodeMap(Boolean CaseInsensitive) + { + this.CaseInsensitive = CaseInsensitive; + StringComparer Comparer = CaseInsensitive ? + StringComparer.OrdinalIgnoreCase : StringComparer.Ordinal; + Set = new SortedSet(Comparer); + Map = new Dictionary(Comparer); + } + public int Count() + { + return Map.Count; + } + public FileNode Get(String FileName) + { + return Map[FileName]; + } + public FileNode GetMain(String FileName) + { + int Index = FileName.IndexOf(':'); + if (0 > Index) + return null; + return Map[FileName.Substring(Index + 1)]; + } + public FileNode GetParent(String FileName, out Int32 Result) + { + FileNode FileNode = Map[Path.GetDirectoryName(FileName)]; + if (null == FileNode) + { + Result = FileSystemBase.STATUS_OBJECT_PATH_NOT_FOUND; + return null; + } + if (0 == (FileNode.FileInfo.FileAttributes & (UInt32)FileAttributes.Directory)) + { + Result = FileSystemBase.STATUS_NOT_A_DIRECTORY; + return null; + } + Result = FileSystemBase.STATUS_SUCCESS; + return FileNode; + } + public void TouchParent(FileNode FileNode) + { + if ("\\" == FileNode.FileName) + return; + Int32 Result; + FileNode Parent = GetParent(FileNode.FileName, out Result); + if (null == Parent) + return; + Parent.FileInfo.LastAccessTime = + Parent.FileInfo.LastWriteTime = + Parent.FileInfo.ChangeTime = (UInt64)DateTime.Now.ToFileTimeUtc(); + } + public Int32 Insert(FileNode FileNode) + { + Set.Add(FileNode.FileName); + Map.Add(FileNode.FileName, FileNode); + TouchParent(FileNode); + return FileSystemBase.STATUS_SUCCESS; + } + public void Remove(FileNode FileNode) + { + if (Set.Remove(FileNode.FileName)) + { + Map.Remove(FileNode.FileName); + TouchParent(FileNode); + } + } + public Boolean HasChild(FileNode FileNode) + { + String MinName = FileNode.FileName + "\\"; + String MaxName = FileNode.FileName + "]"; + SortedSet View = Set.GetViewBetween(MinName, MaxName); + foreach (String Name in View) + if (Name != MaxName) + return true; + return false; + } + public IEnumerator GetChildrenFileNames(FileNode FileNode) + { + String MinName = FileNode.FileName + "\\"; + String MaxName = FileNode.FileName + "]"; + SortedSet View = Set.GetViewBetween(MinName, MaxName); + foreach (String Name in View) + if (Name.StartsWith(MinName, CaseInsensitive ? + StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal)) + yield return Name; + } + public IEnumerator GetStreamNames(FileNode FileNode) + { + String MinName = FileNode.FileName + ":"; + String MaxName = FileNode.FileName + ";"; + SortedSet View = Set.GetViewBetween(MinName, MaxName); + foreach (String Name in View) + if (Name != MaxName) + yield return Name; + } + public IEnumerator GetDescendantFileNames(FileNode FileNode) + { + String MinName = FileNode.FileName + "\\"; + String MaxName = FileNode.FileName + "]"; + SortedSet View = Set.GetViewBetween(MinName, MaxName); + foreach (String Name in View) + if (Name != MaxName) + yield return Name; + } + + private Boolean CaseInsensitive; + private SortedSet Set; + private Dictionary Map; + } + + class Memfs : FileSystemBase + { + public override Int32 GetVolumeInfo( + out VolumeInfo VolumeInfo) + { + VolumeInfo = default(VolumeInfo); + return STATUS_INVALID_DEVICE_REQUEST; + } + public override Int32 SetVolumeLabel( + String VolumeLabel, + out VolumeInfo VolumeInfo) + { + VolumeInfo = default(VolumeInfo); + return STATUS_INVALID_DEVICE_REQUEST; + } + public override Int32 GetSecurityByName( + String FileName, + out UInt32 FileAttributes/* or ReparsePointIndex */, + ref Byte[] SecurityDescriptor) + { + FileAttributes = default(UInt32); + return STATUS_INVALID_DEVICE_REQUEST; + } + public override Int32 Create( + String FileName, + UInt32 CreateOptions, + UInt32 GrantedAccess, + UInt32 FileAttributes, + Byte[] SecurityDescriptor, + UInt64 AllocationSize, + out Object FileNode, + out Object FileDesc, + out FileInfo FileInfo, + out String NormalizedName) + { + FileNode = default(Object); + FileDesc = default(Object); + FileInfo = default(FileInfo); + NormalizedName = default(String); + return STATUS_INVALID_DEVICE_REQUEST; + } + public override Int32 Open( + String FileName, + UInt32 CreateOptions, + UInt32 GrantedAccess, + out Object FileNode, + out Object FileDesc, + out FileInfo FileInfo, + out String NormalizedName) + { + FileNode = default(Object); + FileDesc = default(Object); + FileInfo = default(FileInfo); + NormalizedName = default(String); + return STATUS_INVALID_DEVICE_REQUEST; + } + public override Int32 Overwrite( + Object FileNode, + Object FileDesc, + UInt32 FileAttributes, + Boolean ReplaceFileAttributes, + UInt64 AllocationSize, + out FileInfo FileInfo) + { + FileInfo = default(FileInfo); + return STATUS_INVALID_DEVICE_REQUEST; + } + public override void Cleanup( + Object FileNode, + Object FileDesc, + String FileName, + UInt32 Flags) + { + } + public override void Close( + Object FileNode, + Object FileDesc) + { + } + public override Int32 Read( + Object FileNode, + Object FileDesc, + IntPtr Buffer, + UInt64 Offset, + UInt32 Length, + out UInt32 BytesTransferred) + { + BytesTransferred = default(UInt32); + return STATUS_INVALID_DEVICE_REQUEST; + } + public override Int32 Write( + Object FileNode, + Object FileDesc, + IntPtr Buffer, + UInt64 Offset, + UInt32 Length, + Boolean WriteToEndOfFile, + Boolean ConstrainedIo, + out UInt32 BytesTransferred, + out FileInfo FileInfo) + { + BytesTransferred = default(UInt32); + FileInfo = default(FileInfo); + return STATUS_INVALID_DEVICE_REQUEST; + } + public override Int32 Flush( + Object FileNode, + Object FileDesc, + out FileInfo FileInfo) + { + FileInfo = default(FileInfo); + return STATUS_INVALID_DEVICE_REQUEST; + } + public override Int32 GetFileInfo( + Object FileNode, + Object FileDesc, + out FileInfo FileInfo) + { + FileInfo = default(FileInfo); + return STATUS_INVALID_DEVICE_REQUEST; + } + public override Int32 SetBasicInfo( + Object FileNode, + Object FileDesc, + UInt32 FileAttributes, + UInt64 CreationTime, + UInt64 LastAccessTime, + UInt64 LastWriteTime, + UInt64 ChangeTime, + out FileInfo FileInfo) + { + FileInfo = default(FileInfo); + return STATUS_INVALID_DEVICE_REQUEST; + } + public override Int32 SetFileSize( + Object FileNode, + Object FileDesc, + UInt64 NewSize, + Boolean SetAllocationSize, + out FileInfo FileInfo) + { + FileInfo = default(FileInfo); + return STATUS_INVALID_DEVICE_REQUEST; + } + public override Int32 CanDelete( + Object FileNode, + Object FileDesc, + String FileName) + { + return STATUS_INVALID_DEVICE_REQUEST; + } + public override Int32 Rename( + Object FileNode, + Object FileDesc, + String FileName, + String NewFileName, + Boolean ReplaceIfExists) + { + return STATUS_INVALID_DEVICE_REQUEST; + } + public override Int32 GetSecurity( + Object FileNode, + Object FileDesc, + ref Byte[] SecurityDescriptor) + { + return STATUS_INVALID_DEVICE_REQUEST; + } + public override Int32 SetSecurity( + Object FileNode, + Object FileDesc, + AccessControlSections Sections, + Byte[] SecurityDescriptor) + { + return STATUS_INVALID_DEVICE_REQUEST; + } + public override Boolean ReadDirectoryEntry( + Object FileNode, + Object FileDesc, + String Pattern, + String Marker, + ref Object Context, + out String FileName, + out FileInfo FileInfo) + { + FileName = default(String); + FileInfo = default(FileInfo); + return false; + } + public override Int32 GetReparsePointByName( + String FileName, + Boolean IsDirectory, + IntPtr Buffer, + ref UIntPtr Size) + { + return STATUS_INVALID_DEVICE_REQUEST; + } + public override Int32 GetReparsePoint( + Object FileNode, + Object FileDesc, + String FileName, + IntPtr Buffer, + out UIntPtr Size) + { + Size = default(UIntPtr); + return STATUS_INVALID_DEVICE_REQUEST; + } + public override Int32 SetReparsePoint( + Object FileNode, + Object FileDesc, + String FileName, + IntPtr Buffer, + UIntPtr Size) + { + return STATUS_INVALID_DEVICE_REQUEST; + } + public override Int32 DeleteReparsePoint( + Object FileNode, + Object FileDesc, + String FileName, + IntPtr Buffer, + UIntPtr Size) + { + return STATUS_INVALID_DEVICE_REQUEST; + } + public override Int32 GetStreamInfo( + Object FileNode, + Object FileDesc, + IntPtr Buffer, + UInt32 Length, + out UInt32 BytesTransferred) + { + BytesTransferred = default(UInt32); + return STATUS_INVALID_DEVICE_REQUEST; + } + } + + class MemfsService : Service + { + private class CommandLineUsageException : Exception + { + public CommandLineUsageException(String Message = null) : base(Message) + { + HasMessage = null != Message; + } + + public bool HasMessage; + } + + private const String PROGNAME = "memfs-dotnet"; + + public MemfsService() : base("MemfsService") + { + } + } + + class Program + { + static void Main(string[] args) + { + Environment.ExitCode = new MemfsService().Run(); + } + } +} + +#if false +using System.Collections; +using System.IO; +using System.Runtime.InteropServices; +using System.Security.AccessControl; + +namespace memfs +{ + class Memfs : FileSystemBase + { + protected const int ALLOCATION_UNIT = 4096; + + protected static void ThrowIoExceptionWithHResult(Int32 HResult) + { + throw new IOException(null, HResult); + } + protected static void ThrowIoExceptionWithWin32(Int32 Error) + { + ThrowIoExceptionWithHResult(unchecked((Int32)(0x80070000 | Error))); + } + protected static void ThrowIoExceptionWithNtStatus(Int32 Status) + { + ThrowIoExceptionWithWin32((Int32)Win32FromNtStatus(Status)); + } + + protected class FileDesc + { + public FileStream Stream; + public DirectoryInfo DirInfo; + public DictionaryEntry[] FileSystemInfos; + + public FileDesc(FileStream Stream) + { + this.Stream = Stream; + } + public FileDesc(DirectoryInfo DirInfo) + { + this.DirInfo = DirInfo; + } + public static void GetFileInfoFromFileSystemInfo( + FileSystemInfo Info, + out FileInfo FileInfo) + { + FileInfo.FileAttributes = (UInt32)Info.Attributes; + FileInfo.ReparseTag = 0; + FileInfo.FileSize = Info is System.IO.FileInfo ? + (UInt64)((System.IO.FileInfo)Info).Length : 0; + FileInfo.AllocationSize = (FileInfo.FileSize + ALLOCATION_UNIT - 1) + / ALLOCATION_UNIT * ALLOCATION_UNIT; + FileInfo.CreationTime = (UInt64)Info.CreationTimeUtc.ToFileTimeUtc(); + FileInfo.LastAccessTime = (UInt64)Info.LastAccessTimeUtc.ToFileTimeUtc(); + FileInfo.LastWriteTime = (UInt64)Info.LastWriteTimeUtc.ToFileTimeUtc(); + FileInfo.ChangeTime = FileInfo.LastWriteTime; + FileInfo.IndexNumber = 0; + FileInfo.HardLinks = 0; + } + public Int32 GetFileInfo(out FileInfo FileInfo) + { + if (null != Stream) + { + BY_HANDLE_FILE_INFORMATION Info; + if (!GetFileInformationByHandle(Stream.SafeFileHandle.DangerousGetHandle(), + out Info)) + ThrowIoExceptionWithWin32(Marshal.GetLastWin32Error()); + FileInfo.FileAttributes = Info.dwFileAttributes; + FileInfo.ReparseTag = 0; + FileInfo.FileSize = (UInt64)Stream.Length; + FileInfo.AllocationSize = (FileInfo.FileSize + ALLOCATION_UNIT - 1) + / ALLOCATION_UNIT * ALLOCATION_UNIT; + FileInfo.CreationTime = Info.ftCreationTime; + FileInfo.LastAccessTime = Info.ftLastAccessTime; + FileInfo.LastWriteTime = Info.ftLastWriteTime; + FileInfo.ChangeTime = FileInfo.LastWriteTime; + FileInfo.IndexNumber = 0; + FileInfo.HardLinks = 0; + } + else + GetFileInfoFromFileSystemInfo(DirInfo, out FileInfo); + return STATUS_SUCCESS; + } + public void SetBasicInfo( + UInt32 FileAttributes, + UInt64 CreationTime, + UInt64 LastAccessTime, + UInt64 LastWriteTime) + { + if (0 == FileAttributes) + FileAttributes = (UInt32)System.IO.FileAttributes.Normal; + if (null != Stream) + { + FILE_BASIC_INFO Info = default(FILE_BASIC_INFO); + if (unchecked((UInt32)(-1)) != FileAttributes) + Info.FileAttributes = FileAttributes; + if (0 != CreationTime) + Info.CreationTime = CreationTime; + if (0 != LastAccessTime) + Info.LastAccessTime = LastAccessTime; + if (0 != LastWriteTime) + Info.LastWriteTime = LastWriteTime; + if (!SetFileInformationByHandle(Stream.SafeFileHandle.DangerousGetHandle(), + 0/*FileBasicInfo*/, ref Info, (UInt32)Marshal.SizeOf(Info))) + ThrowIoExceptionWithWin32(Marshal.GetLastWin32Error()); + } + else + { + if (unchecked((UInt32)(-1)) != FileAttributes) + DirInfo.Attributes = (System.IO.FileAttributes)FileAttributes; + if (0 != CreationTime) + DirInfo.CreationTimeUtc = DateTime.FromFileTimeUtc((Int64)CreationTime); + if (0 != LastAccessTime) + DirInfo.LastAccessTimeUtc = DateTime.FromFileTimeUtc((Int64)LastAccessTime); + if (0 != LastWriteTime) + DirInfo.LastWriteTimeUtc = DateTime.FromFileTimeUtc((Int64)LastWriteTime); + } + } + public UInt32 GetFileAttributes() + { + FileInfo FileInfo; + GetFileInfo(out FileInfo); + return FileInfo.FileAttributes; + } + public void SetFileAttributes(UInt32 FileAttributes) + { + SetBasicInfo(FileAttributes, 0, 0, 0); + } + public Byte[] GetSecurityDescriptor() + { + if (null != Stream) + return Stream.GetAccessControl().GetSecurityDescriptorBinaryForm(); + else + return DirInfo.GetAccessControl().GetSecurityDescriptorBinaryForm(); + } + public void SetSecurityDescriptor(AccessControlSections Sections, Byte[] SecurityDescriptor) + { + Int32 SecurityInformation = 0; + if (0 != (Sections & AccessControlSections.Owner)) + SecurityInformation |= 1/*OWNER_SECURITY_INFORMATION*/; + if (0 != (Sections & AccessControlSections.Group)) + SecurityInformation |= 2/*GROUP_SECURITY_INFORMATION*/; + if (0 != (Sections & AccessControlSections.Access)) + SecurityInformation |= 4/*DACL_SECURITY_INFORMATION*/; + if (0 != (Sections & AccessControlSections.Audit)) + SecurityInformation |= 8/*SACL_SECURITY_INFORMATION*/; + if (null != Stream) + { + if (!SetKernelObjectSecurity(Stream.SafeFileHandle.DangerousGetHandle(), + SecurityInformation, SecurityDescriptor)) + ThrowIoExceptionWithWin32(Marshal.GetLastWin32Error()); + } + else + { + if (!SetFileSecurityW(DirInfo.FullName, + SecurityInformation, SecurityDescriptor)) + ThrowIoExceptionWithWin32(Marshal.GetLastWin32Error()); + } + } + public void SetDisposition(Boolean Safe) + { + if (null != Stream) + { + FILE_DISPOSITION_INFO Info; + Info.DeleteFile = true; + if (!SetFileInformationByHandle(Stream.SafeFileHandle.DangerousGetHandle(), + 4/*FileDispositionInfo*/, ref Info, (UInt32)Marshal.SizeOf(Info))) + if (!Safe) + ThrowIoExceptionWithWin32(Marshal.GetLastWin32Error()); + } + else + try + { + DirInfo.Delete(); + } + catch (Exception ex) + { + if (!Safe) + ThrowIoExceptionWithHResult(ex.HResult); + } + } + public static void Rename(String FileName, String NewFileName, Boolean ReplaceIfExists) + { + if (!MoveFileExW(FileName, NewFileName, ReplaceIfExists ? 1U/*MOVEFILE_REPLACE_EXISTING*/ : 0)) + ThrowIoExceptionWithWin32(Marshal.GetLastWin32Error()); + } + + /* interop */ + [StructLayout(LayoutKind.Sequential, Pack = 4)] + private struct BY_HANDLE_FILE_INFORMATION + { + public UInt32 dwFileAttributes; + public UInt64 ftCreationTime; + public UInt64 ftLastAccessTime; + public UInt64 ftLastWriteTime; + public UInt32 dwVolumeSerialNumber; + public UInt32 nFileSizeHigh; + public UInt32 nFileSizeLow; + public UInt32 nNumberOfLinks; + public UInt32 nFileIndexHigh; + public UInt32 nFileIndexLow; + } + [StructLayout(LayoutKind.Sequential)] + private struct FILE_BASIC_INFO + { + public UInt64 CreationTime; + public UInt64 LastAccessTime; + public UInt64 LastWriteTime; + public UInt64 ChangeTime; + public UInt32 FileAttributes; + } + [StructLayout(LayoutKind.Sequential)] + private struct FILE_DISPOSITION_INFO + { + public Boolean DeleteFile; + } + [DllImport("kernel32.dll", SetLastError = true)] + private static extern Boolean GetFileInformationByHandle( + IntPtr hFile, + out BY_HANDLE_FILE_INFORMATION lpFileInformation); + [DllImport("kernel32.dll", SetLastError = true)] + private static extern Boolean SetFileInformationByHandle( + IntPtr hFile, + Int32 FileInformationClass, + ref FILE_BASIC_INFO lpFileInformation, + UInt32 dwBufferSize); + [DllImport("kernel32.dll", SetLastError = true)] + private static extern Boolean SetFileInformationByHandle( + IntPtr hFile, + Int32 FileInformationClass, + ref FILE_DISPOSITION_INFO lpFileInformation, + UInt32 dwBufferSize); + [DllImport("kernel32.dll", SetLastError = true)] + private static extern Boolean MoveFileExW( + [MarshalAs(UnmanagedType.LPWStr)] String lpExistingFileName, + [MarshalAs(UnmanagedType.LPWStr)] String lpNewFileName, + UInt32 dwFlags); + [DllImport("advapi32.dll", SetLastError = true)] + private static extern Boolean SetFileSecurityW( + [MarshalAs(UnmanagedType.LPWStr)] String FileName, + Int32 SecurityInformation, + Byte[] SecurityDescriptor); + [DllImport("advapi32.dll", SetLastError = true)] + private static extern Boolean SetKernelObjectSecurity( + IntPtr Handle, + Int32 SecurityInformation, + Byte[] SecurityDescriptor); + } + + private class DirectoryEntryComparer : IComparer + { + public int Compare(object x, object y) + { + return String.Compare( + (String)((DictionaryEntry)x).Key, + (String)((DictionaryEntry)y).Key); + } + } + private static DirectoryEntryComparer _DirectoryEntryComparer = + new DirectoryEntryComparer(); + + public Memfs(String Path0) + { + _Path = Path.GetFullPath(Path0); + if (_Path.EndsWith("\\")) + _Path = _Path.Substring(0, _Path.Length - 1); + } + public String ConcatPath(String FileName) + { + return _Path + FileName; + } + public 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; + } + public override Int32 Init(Object Host0) + { + FileSystemHost Host = (FileSystemHost)Host0; + Host.SectorSize = ALLOCATION_UNIT; + Host.SectorsPerAllocationUnit = 1; + Host.MaxComponentLength = 255; + Host.FileInfoTimeout = 1000; + Host.CaseSensitiveSearch = false; + Host.CasePreservedNames = true; + Host.UnicodeOnDisk = true; + Host.PersistentAcls = true; + Host.PostCleanupWhenModifiedOnly = true; + Host.PassQueryDirectoryPattern = true; + Host.VolumeCreationTime = (UInt64)File.GetCreationTimeUtc(_Path).ToFileTimeUtc(); + Host.VolumeSerialNumber = 0; + return STATUS_SUCCESS; + } + public 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; + } + public 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; + } + public 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 = null; + try + { + FileName = ConcatPath(FileName); + if (0 == (CreateOptions & FILE_DIRECTORY_FILE)) + { + FileSecurity Security = null; + if (null != SecurityDescriptor) + { + Security = new FileSecurity(); + Security.SetSecurityDescriptorBinaryForm(SecurityDescriptor); + } + FileDesc = new FileDesc( + new FileStream( + FileName, + FileMode.CreateNew, + (FileSystemRights)GrantedAccess | FileSystemRights.WriteAttributes, + FileShare.Read | FileShare.Write | FileShare.Delete, + 4096, + 0, + Security)); + } + else + { + if (Directory.Exists(FileName)) + ThrowIoExceptionWithNtStatus(STATUS_OBJECT_NAME_COLLISION); + DirectorySecurity Security = null; + if (null != SecurityDescriptor) + { + Security = new DirectorySecurity(); + Security.SetSecurityDescriptorBinaryForm(SecurityDescriptor); + } + FileDesc = new FileDesc( + Directory.CreateDirectory(FileName, Security)); + } + FileDesc.SetFileAttributes(FileAttributes); + FileNode = default(Object); + FileDesc0 = FileDesc; + NormalizedName = default(String); + return FileDesc.GetFileInfo(out FileInfo); + } + catch + { + if (null != FileDesc && null != FileDesc.Stream) + FileDesc.Stream.Dispose(); + throw; + } + } + public override Int32 Open( + String FileName, + UInt32 CreateOptions, + UInt32 GrantedAccess, + out Object FileNode, + out Object FileDesc0, + out FileInfo FileInfo, + out String NormalizedName) + { + FileDesc FileDesc = null; + try + { + FileName = ConcatPath(FileName); + if (!Directory.Exists(FileName)) + { + FileDesc = new FileDesc( + new FileStream( + FileName, + FileMode.Open, + (FileSystemRights)GrantedAccess, + FileShare.Read | FileShare.Write | FileShare.Delete, + 4096, + 0)); + } + else + { + FileDesc = new FileDesc( + new DirectoryInfo(FileName)); + } + FileNode = default(Object); + FileDesc0 = FileDesc; + NormalizedName = default(String); + return FileDesc.GetFileInfo(out FileInfo); + } + catch + { + if (null != FileDesc && null != FileDesc.Stream) + FileDesc.Stream.Dispose(); + throw; + } + } + public override Int32 Overwrite( + Object FileNode, + Object FileDesc0, + UInt32 FileAttributes, + Boolean ReplaceFileAttributes, + UInt64 AllocationSize, + out FileInfo FileInfo) + { + FileDesc FileDesc = (FileDesc)FileDesc0; + if (ReplaceFileAttributes) + FileDesc.SetFileAttributes(FileAttributes); + else if (0 != FileAttributes) + FileDesc.SetFileAttributes(FileDesc.GetFileAttributes() | FileAttributes); + FileDesc.Stream.SetLength(0); + return FileDesc.GetFileInfo(out FileInfo); + } + public override void Cleanup( + Object FileNode, + Object FileDesc0, + String FileName, + UInt32 Flags) + { + FileDesc FileDesc = (FileDesc)FileDesc0; + if (0 != (Flags & CleanupDelete)) + { + FileDesc.SetDisposition(true); + if (null != FileDesc.Stream) + FileDesc.Stream.Dispose(); + } + } + public override void Close( + Object FileNode, + Object FileDesc0) + { + FileDesc FileDesc = (FileDesc)FileDesc0; + if (null != FileDesc.Stream) + FileDesc.Stream.Dispose(); + } + public override Int32 Read( + Object FileNode, + Object FileDesc0, + IntPtr Buffer, + UInt64 Offset, + UInt32 Length, + out UInt32 PBytesTransferred) + { + FileDesc FileDesc = (FileDesc)FileDesc0; + if (Offset >= (UInt64)FileDesc.Stream.Length) + ThrowIoExceptionWithNtStatus(STATUS_END_OF_FILE); + 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; + } + public 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; + if (ConstrainedIo) + { + if (Offset >= (UInt64)FileDesc.Stream.Length) + { + PBytesTransferred = default(UInt32); + FileInfo = default(FileInfo); + return STATUS_SUCCESS; + } + if (Offset + Length > (UInt64)FileDesc.Stream.Length) + Length = (UInt32)((UInt64)FileDesc.Stream.Length - Offset); + } + Byte[] Bytes = new byte[Length]; + Marshal.Copy(Buffer, Bytes, 0, Bytes.Length); + if (!WriteToEndOfFile) + FileDesc.Stream.Seek((Int64)Offset, SeekOrigin.Begin); + FileDesc.Stream.Write(Bytes, 0, Bytes.Length); + PBytesTransferred = (UInt32)Bytes.Length; + return FileDesc.GetFileInfo(out FileInfo); + } + public 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 FileDesc.GetFileInfo(out FileInfo); + } + public override Int32 GetFileInfo( + Object FileNode, + Object FileDesc0, + out FileInfo FileInfo) + { + FileDesc FileDesc = (FileDesc)FileDesc0; + return FileDesc.GetFileInfo(out FileInfo); + } + public override Int32 SetBasicInfo( + Object FileNode, + Object FileDesc0, + UInt32 FileAttributes, + UInt64 CreationTime, + UInt64 LastAccessTime, + UInt64 LastWriteTime, + UInt64 ChangeTime, + out FileInfo FileInfo) + { + FileDesc FileDesc = (FileDesc)FileDesc0; + FileDesc.SetBasicInfo(FileAttributes, CreationTime, LastAccessTime, LastWriteTime); + return FileDesc.GetFileInfo(out FileInfo); + } + public override Int32 SetFileSize( + Object FileNode, + Object FileDesc0, + UInt64 NewSize, + Boolean SetAllocationSize, + out FileInfo FileInfo) + { + FileDesc FileDesc = (FileDesc)FileDesc0; + if (!SetAllocationSize || (UInt64)FileDesc.Stream.Length > 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); + } + return FileDesc.GetFileInfo(out FileInfo); + } + public override Int32 CanDelete( + Object FileNode, + Object FileDesc0, + String FileName) + { + FileDesc FileDesc = (FileDesc)FileDesc0; + FileDesc.SetDisposition(false); + return STATUS_SUCCESS; + } + public override Int32 Rename( + Object FileNode, + Object FileDesc0, + String FileName, + String NewFileName, + Boolean ReplaceIfExists) + { + FileName = ConcatPath(FileName); + NewFileName = ConcatPath(NewFileName); + FileDesc.Rename(FileName, NewFileName, ReplaceIfExists); + return STATUS_SUCCESS; + } + public override Int32 GetSecurity( + Object FileNode, + Object FileDesc0, + ref Byte[] SecurityDescriptor) + { + FileDesc FileDesc = (FileDesc)FileDesc0; + SecurityDescriptor = FileDesc.GetSecurityDescriptor(); + return STATUS_SUCCESS; + } + public override Int32 SetSecurity( + Object FileNode, + Object FileDesc0, + AccessControlSections Sections, + Byte[] SecurityDescriptor) + { + FileDesc FileDesc = (FileDesc)FileDesc0; + FileDesc.SetSecurityDescriptor(Sections, SecurityDescriptor); + return STATUS_SUCCESS; + } + public override Boolean ReadDirectoryEntry( + Object FileNode, + Object FileDesc0, + String Pattern, + String Marker, + ref Object Context, + out String FileName, + out FileInfo FileInfo) + { + FileDesc FileDesc = (FileDesc)FileDesc0; + if (null == FileDesc.FileSystemInfos) + { + IEnumerable Enum = FileDesc.DirInfo.EnumerateFileSystemInfos( + null != Pattern ? Pattern : "*"); + SortedList List = new SortedList(); + List.Add(".", FileDesc.DirInfo); + List.Add("..", FileDesc.DirInfo.Parent); + foreach (FileSystemInfo Info in Enum) + List.Add(Info.Name, Info); + FileDesc.FileSystemInfos = new DictionaryEntry[List.Count]; + List.CopyTo(FileDesc.FileSystemInfos, 0); + } + int Index; + if (null == Context) + { + Index = 0; + if (null != Marker) + { + Index = Array.BinarySearch(FileDesc.FileSystemInfos, + new DictionaryEntry(Marker, null), + _DirectoryEntryComparer); + if (0 <= Index) + Index++; + else + Index = ~Index; + } + } + else + Index = (int)Context; + if (FileDesc.FileSystemInfos.Length > Index) + { + Context = Index + 1; + FileName = (String)FileDesc.FileSystemInfos[Index].Key; + FileDesc.GetFileInfoFromFileSystemInfo( + (FileSystemInfo)FileDesc.FileSystemInfos[Index].Value, + out FileInfo); + return true; + } + else + { + FileName = default(String); + FileInfo = default(FileInfo); + return false; + } + } + + private String _Path; + } + + class MemfsService : Service + { + protected override void OnStart(String[] Args) + { + try + { + String DebugLogFile = null; + UInt32 DebugFlags = 0; + String VolumePrefix = null; + String PassThrough = null; + String MountPoint = null; + IntPtr DebugLogHandle = (IntPtr)(-1); + FileSystemHost Host = null; + Memfs Memfs = null; + int I; + + for (I = 1; Args.Length > I; I++) + { + String Arg = Args[I]; + if ('-' != Arg[0]) + break; + switch (Arg[1]) + { + case '?': + throw new CommandLineUsageException(); + case 'd': + argtol(Args, ref I, ref DebugFlags); + break; + case 'D': + argtos(Args, ref I, ref DebugLogFile); + break; + case 'm': + argtos(Args, ref I, ref MountPoint); + break; + case 'p': + argtos(Args, ref I, ref PassThrough); + break; + case 'u': + argtos(Args, ref I, ref VolumePrefix); + break; + default: + throw new CommandLineUsageException(); + } + } + + if (Args.Length > I) + throw new CommandLineUsageException(); + + if (null == PassThrough && null != VolumePrefix) + { + I = VolumePrefix.IndexOf('\\'); + if (-1 != I && VolumePrefix.Length > I && '\\' != VolumePrefix[I + 1]) + { + I = VolumePrefix.IndexOf('\\', I + 1); + if (-1 != I && + VolumePrefix.Length > I + 1 && + ( + ('A' <= VolumePrefix[I + 1] && VolumePrefix[I + 1] <= 'Z') || + ('a' <= VolumePrefix[I + 1] && VolumePrefix[I + 1] <= 'z') + ) && + '$' == VolumePrefix[I + 2]) + { + PassThrough = String.Format("{0}:{1}", VolumePrefix[I + 1], VolumePrefix.Substring(I + 3)); + } + } + } + + if (null == PassThrough || null == MountPoint) + throw new CommandLineUsageException(); + + if (null != DebugLogFile) + if (0 > FileSystemHost.SetDebugLogFile(DebugLogFile)) + throw new CommandLineUsageException("cannot open debug log file"); + + Host = new FileSystemHost(Memfs = new Memfs(PassThrough)); + Host.Prefix = VolumePrefix; + if (0 > Host.Mount(MountPoint, null, true, DebugFlags)) + throw new IOException("cannot mount file system"); + MountPoint = Host.MountPoint(); + _Host = Host; + + Log(EVENTLOG_INFORMATION_TYPE, String.Format("{0}{1}{2} -p {3} -m {4}", + PROGNAME, + null != VolumePrefix && 0 < VolumePrefix.Length ? " -u " : "", + null != VolumePrefix && 0 < VolumePrefix.Length ? VolumePrefix : "", + PassThrough, + MountPoint)); + } + catch (CommandLineUsageException ex) + { + Log(EVENTLOG_ERROR_TYPE, String.Format( + "{0}" + + "usage: {1} 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", + ex.HasMessage ? ex.Message + "\n" : "", + PROGNAME)); + throw; + } + catch (Exception ex) + { + Log(EVENTLOG_ERROR_TYPE, String.Format("{0}", ex.Message)); + throw; + } + } + protected override void OnStop() + { + _Host.Unmount(); + _Host = null; + } + + private static void argtos(String[] Args, ref int I, ref String V) + { + if (Args.Length > ++I) + V = Args[I]; + else + throw new CommandLineUsageException(); + } + private static void argtol(String[] Args, ref int I, ref UInt32 V) + { + Int32 R; + if (Args.Length > ++I) + V = Int32.TryParse(Args[I], out R) ? (UInt32)R : V; + else + throw new CommandLineUsageException(); + } + + private FileSystemHost _Host; + } +} +#endif