├── .gitignore ├── LICENSE ├── README.md ├── WinAVFS.CLI ├── 7z_amd64.dll ├── 7z_x86.dll ├── Program.cs ├── Properties │ └── AssemblyInfo.cs └── WinAVFS.CLI.csproj ├── WinAVFS.Core ├── ConcurrentObjectPool.cs ├── FSTree.cs ├── FSTreeNode.cs ├── IArchiveProvider.cs ├── ReadOnlyAVFS.cs ├── SevenZipProvider.cs ├── WinAVFS.Core.csproj └── ZipArchiveProvider.cs └── WinAVFS.sln /.gitignore: -------------------------------------------------------------------------------- 1 | bin/ 2 | obj/ 3 | /packages/ 4 | riderModule.iml 5 | /.idea/ 6 | /.vs/ 7 | /_ReSharper.Caches/ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 DeepAQ 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WinAVFS 2 | A high-performance Windows virtual filesystem for mounting archives 3 | 4 | ## What is WinAVFS 5 | WinAVFS is a user-mode Windows filesystem implementation based on Dokan driver. It mounts an archive file (supports all formats supported by 7-Zip) as a read-only volume. To achieve high overall performance, WinAVFS uses unmanaged memory as cached buffer and extracts a file only when it is being read. 6 | 7 | ## Dependencies 8 | - [Dokany](https://github.com/dokan-dev/dokany) file system driver [LGPL License] 9 | - [DokanNet](https://github.com/dokan-dev/dokan-dotnet) [MIT License] 10 | - [SevenZipSharp](https://github.com/squid-box/SevenZipSharp) [LGPL License] 11 | - 7z.dll from [7-Zip](https://www.7-zip.org) [LGPL + unRAR + BSD 3-clause License] 12 | 13 | ## Usage 14 | To mount an archive, run the following command: 15 | ``` 16 | > WinAVFS.CLI.exe 17 | ``` 18 | While the application is running, press Ctrl-C to unmount the volume. 19 | 20 | ### Mount an archive as a volume with drive letter 21 | ``` 22 | > WinAVFS.CLI.exe D:\test.zip Z:\ 23 | ``` 24 | 25 | ### Mount an archive in an NTFS mountpoint 26 | ``` 27 | > WinAVFS.CLI.exe D:\test.zip D:\mount 28 | ``` 29 | 30 | ## License 31 | MIT License 32 | 33 | Copyright (c) 2020 DeepAQ 34 | 35 | Permission is hereby granted, free of charge, to any person obtaining a copy 36 | of this software and associated documentation files (the "Software"), to deal 37 | in the Software without restriction, including without limitation the rights 38 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 39 | copies of the Software, and to permit persons to whom the Software is 40 | furnished to do so, subject to the following conditions: 41 | 42 | The above copyright notice and this permission notice shall be included in all 43 | copies or substantial portions of the Software. 44 | 45 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 46 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 47 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 48 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 49 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 50 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 51 | SOFTWARE. 52 | -------------------------------------------------------------------------------- /WinAVFS.CLI/7z_amd64.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DeepAQ/WinAVFS/2dc024ca5ad727a5dd6981694d7caa1509354dfd/WinAVFS.CLI/7z_amd64.dll -------------------------------------------------------------------------------- /WinAVFS.CLI/7z_x86.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DeepAQ/WinAVFS/2dc024ca5ad727a5dd6981694d7caa1509354dfd/WinAVFS.CLI/7z_x86.dll -------------------------------------------------------------------------------- /WinAVFS.CLI/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using WinAVFS.Core; 3 | 4 | namespace WinAVFS.CLI 5 | { 6 | internal class Program 7 | { 8 | public static void Main(string[] args) 9 | { 10 | if (args.Length != 2) 11 | { 12 | Console.WriteLine(@"Usage: WinAVFS.CLI.exe "); 13 | Console.WriteLine(@"Example: WinAVFS.CLI.exe D:\1.zip Z:\"); 14 | return; 15 | } 16 | 17 | var fs = new ReadOnlyAVFS(new SevenZipProvider(args[0])); 18 | fs.Mount(args[1]); 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /WinAVFS.CLI/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | 4 | // General Information about an assembly is controlled through the following 5 | // set of attributes. Change these attribute values to modify the information 6 | // associated with an assembly. 7 | [assembly: AssemblyTitle("WinAVFS.CLI")] 8 | [assembly: AssemblyDescription("")] 9 | [assembly: AssemblyConfiguration("")] 10 | [assembly: AssemblyCompany("")] 11 | [assembly: AssemblyProduct("WinAVFS.CLI")] 12 | [assembly: AssemblyCopyright("Copyright © DeepAQ 2020")] 13 | [assembly: AssemblyTrademark("")] 14 | [assembly: AssemblyCulture("")] 15 | 16 | // Setting ComVisible to false makes the types in this assembly not visible 17 | // to COM components. If you need to access a type in this assembly from 18 | // COM, set the ComVisible attribute to true on that type. 19 | [assembly: ComVisible(false)] 20 | 21 | // The following GUID is for the ID of the typelib if this project is exposed to COM 22 | [assembly: Guid("0769F9F7-0BC5-4A7C-A8DC-BAD02D4C8E6B")] 23 | 24 | // Version information for an assembly consists of the following four values: 25 | // 26 | // Major Version 27 | // Minor Version 28 | // Build Number 29 | // Revision 30 | // 31 | // You can specify all the values or you can default the Build and Revision Numbers 32 | // by using the '*' as shown below: 33 | // [assembly: AssemblyVersion("1.0.*")] 34 | [assembly: AssemblyVersion("1.0.0.0")] 35 | [assembly: AssemblyFileVersion("1.0.0.0")] -------------------------------------------------------------------------------- /WinAVFS.CLI/WinAVFS.CLI.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {0769F9F7-0BC5-4A7C-A8DC-BAD02D4C8E6B} 8 | Exe 9 | Properties 10 | WinAVFS.CLI 11 | WinAVFS.CLI 12 | v4.6 13 | 512 14 | default 15 | 16 | 17 | pdbonly 18 | true 19 | TRACE 20 | prompt 21 | 4 22 | bin\x64\Release\ 23 | x64 24 | 25 | 26 | true 27 | full 28 | false 29 | DEBUG;TRACE 30 | prompt 31 | 4 32 | bin\x64\Debug\ 33 | x64 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | {0feb6b9c-fd58-4f32-b704-f1a85f967340} 49 | WinAVFS.Core 50 | 51 | 52 | 53 | 54 | 7z.dll 55 | Always 56 | 57 | 58 | 7z64.dll 59 | Always 60 | 61 | 62 | 63 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /WinAVFS.Core/ConcurrentObjectPool.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Concurrent; 3 | using System.Collections.Generic; 4 | 5 | namespace WinAVFS.Core 6 | { 7 | public class ConcurrentObjectPool 8 | { 9 | private readonly ConcurrentBag pool; 10 | private readonly Func factory; 11 | 12 | public ConcurrentObjectPool(Func factory) 13 | { 14 | this.pool = new ConcurrentBag(); 15 | this.factory = factory; 16 | } 17 | 18 | public T Get() 19 | { 20 | return this.pool.TryTake(out var item) ? item : factory(); 21 | } 22 | 23 | public void Put(T item) 24 | { 25 | this.pool.Add(item); 26 | } 27 | 28 | public T[] GetAll() 29 | { 30 | return this.pool.ToArray(); 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /WinAVFS.Core/FSTree.cs: -------------------------------------------------------------------------------- 1 | namespace WinAVFS.Core 2 | { 3 | public class FSTree 4 | { 5 | public FSTreeNode Root { get; set; } 6 | } 7 | } -------------------------------------------------------------------------------- /WinAVFS.Core/FSTreeNode.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Runtime.InteropServices; 4 | 5 | namespace WinAVFS.Core 6 | { 7 | public class FSTreeNode 8 | { 9 | public FSTreeNode Parent { get; private set; } 10 | 11 | public string Name { get; private set; } = ""; 12 | 13 | public string FullName { get; private set; } = ""; 14 | 15 | public long Length { get; private set; } = 0; 16 | 17 | public long CompressedLength { get; private set; } = 0; 18 | 19 | public DateTime? CreationTime { get; internal set; } 20 | 21 | public DateTime? LastAccessTime { get; internal set; } 22 | 23 | public DateTime? LastWriteTime { get; internal set; } 24 | 25 | public Dictionary Children { get; } 26 | 27 | public bool IsDirectory => this.Children != null; 28 | 29 | public object Context { get; internal set; } 30 | 31 | public IntPtr Buffer { get; internal set; } = IntPtr.Zero; 32 | 33 | private bool extracted = false; 34 | 35 | public FSTreeNode() : this(false) 36 | { 37 | } 38 | 39 | public FSTreeNode(bool isDirectory) 40 | { 41 | if (isDirectory) 42 | { 43 | this.Children = new Dictionary(); 44 | } 45 | } 46 | 47 | public FSTreeNode GetOrAddChild(bool isDirectory, string name, long length = 0, long compressedLength = 0, 48 | object context = null) 49 | { 50 | if (this.Children == null) 51 | { 52 | return null; 53 | } 54 | 55 | var caseInsensitiveName = name.ToLower(); 56 | if (this.Children.ContainsKey(caseInsensitiveName)) 57 | { 58 | return this.Children[caseInsensitiveName]; 59 | } 60 | 61 | var child = new FSTreeNode(isDirectory) 62 | { 63 | Parent = this, 64 | Name = name, 65 | FullName = $"{this.FullName}\\{name}", 66 | Length = length, 67 | CompressedLength = compressedLength, 68 | Context = context, 69 | }; 70 | this.Children[caseInsensitiveName] = child; 71 | 72 | if (!isDirectory) 73 | { 74 | var parent = this; 75 | while (parent != null) 76 | { 77 | parent.Length += length; 78 | parent.CompressedLength += compressedLength; 79 | parent = parent.Parent; 80 | } 81 | } 82 | 83 | return child; 84 | } 85 | 86 | public void FillBuffer(Action extractAction) 87 | { 88 | if (this.extracted || this.IsDirectory) 89 | { 90 | return; 91 | } 92 | 93 | lock (this) 94 | { 95 | if (!this.extracted) 96 | { 97 | if (this.Buffer == IntPtr.Zero) 98 | { 99 | this.Buffer = Marshal.AllocHGlobal((IntPtr) this.Length); 100 | } 101 | 102 | try 103 | { 104 | extractAction(this.Buffer); 105 | this.extracted = true; 106 | } 107 | catch (Exception ex) 108 | { 109 | Console.Error.WriteLine(ex.StackTrace); 110 | } 111 | } 112 | } 113 | } 114 | } 115 | } -------------------------------------------------------------------------------- /WinAVFS.Core/IArchiveProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | 4 | namespace WinAVFS.Core 5 | { 6 | public interface IArchiveProvider : IDisposable 7 | { 8 | FSTree ReadFSTree(); 9 | 10 | void ExtractFileUnmanaged(FSTreeNode node, IntPtr buffer); 11 | } 12 | } -------------------------------------------------------------------------------- /WinAVFS.Core/ReadOnlyAVFS.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Concurrent; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Security.AccessControl; 7 | using DokanNet; 8 | using DokanNet.Logging; 9 | using FileAccess = DokanNet.FileAccess; 10 | 11 | namespace WinAVFS.Core 12 | { 13 | public class ReadOnlyAVFS : IDokanOperationsUnsafe 14 | { 15 | private IArchiveProvider archiveProvider; 16 | private FSTree fsTree; 17 | 18 | public ReadOnlyAVFS(IArchiveProvider archiveProvider) 19 | { 20 | this.archiveProvider = archiveProvider; 21 | } 22 | 23 | public void Mount(string mountPoint) 24 | { 25 | this.Unmount(mountPoint); 26 | 27 | this.fsTree = this.archiveProvider.ReadFSTree(); 28 | this.Mount(mountPoint, DokanOptions.WriteProtection | DokanOptions.MountManager, new NullLogger()); 29 | } 30 | 31 | public void Unmount(string mountPoint) 32 | { 33 | Dokan.RemoveMountPoint(mountPoint); 34 | this.fsTree = null; 35 | } 36 | 37 | #region Private helper methods 38 | 39 | private static readonly FileInformation[] EmptyFileInformation = new FileInformation[0]; 40 | private readonly DateTime defaultTime = DateTime.Now; 41 | 42 | private readonly ConcurrentDictionary nodeCache = 43 | new ConcurrentDictionary(); 44 | 45 | private FSTreeNode GetNode(string fileName, IDokanFileInfo info = null) 46 | { 47 | if (info?.Context != null) 48 | { 49 | return (FSTreeNode) info.Context; 50 | } 51 | 52 | fileName = fileName.ToLower(); 53 | if (nodeCache.TryGetValue(fileName, out var nodeFromCache)) 54 | { 55 | return nodeFromCache; 56 | } 57 | 58 | var paths = fileName.Split('\\'); 59 | var node = this.fsTree.Root; 60 | foreach (var path in paths.Where(y => !string.IsNullOrEmpty(y))) 61 | { 62 | if (!node.IsDirectory || !node.Children.ContainsKey(path)) 63 | { 64 | return null; 65 | } 66 | 67 | node = node.Children[path]; 68 | } 69 | 70 | nodeCache.TryAdd(fileName, node); 71 | return node; 72 | } 73 | 74 | #endregion 75 | 76 | #region Dokan filesystem implementation 77 | 78 | public NtStatus CreateFile(string fileName, FileAccess access, FileShare share, FileMode mode, 79 | FileOptions options, FileAttributes attributes, IDokanFileInfo info) 80 | { 81 | if (mode != FileMode.Open && mode != FileMode.OpenOrCreate) 82 | { 83 | return NtStatus.AccessDenied; 84 | } 85 | 86 | if (info.Context == null) 87 | { 88 | var node = this.GetNode(fileName); 89 | if (node == null) 90 | { 91 | if (mode == FileMode.OpenOrCreate) 92 | { 93 | return NtStatus.AccessDenied; 94 | } 95 | 96 | return NtStatus.ObjectPathNotFound; 97 | } 98 | 99 | info.IsDirectory = node.IsDirectory; 100 | info.Context = node; 101 | } 102 | 103 | return NtStatus.Success; 104 | } 105 | 106 | public void Cleanup(string fileName, IDokanFileInfo info) 107 | { 108 | // No-op 109 | } 110 | 111 | public void CloseFile(string fileName, IDokanFileInfo info) 112 | { 113 | // No-op 114 | } 115 | 116 | public NtStatus ReadFile(string fileName, byte[] buffer, out int bytesRead, long offset, IDokanFileInfo info) 117 | { 118 | bytesRead = 0; 119 | return NtStatus.NotImplemented; 120 | } 121 | 122 | public NtStatus ReadFile(string fileName, IntPtr buffer, uint bufferLength, out int bytesRead, long offset, 123 | IDokanFileInfo info) 124 | { 125 | bytesRead = 0; 126 | var node = this.GetNode(fileName, info); 127 | if (node == null) 128 | { 129 | return NtStatus.ObjectPathNotFound; 130 | } 131 | 132 | node.FillBuffer(buf => this.archiveProvider.ExtractFileUnmanaged(node, buf)); 133 | 134 | unsafe 135 | { 136 | bytesRead = (int) Math.Min(bufferLength, node.Length - offset); 137 | Buffer.MemoryCopy((byte*) node.Buffer.ToPointer() + offset, buffer.ToPointer(), bufferLength, 138 | bytesRead); 139 | } 140 | 141 | return NtStatus.Success; 142 | } 143 | 144 | public NtStatus WriteFile(string fileName, byte[] buffer, out int bytesWritten, long offset, 145 | IDokanFileInfo info) 146 | { 147 | bytesWritten = 0; 148 | return NtStatus.AccessDenied; 149 | } 150 | 151 | public NtStatus WriteFile(string fileName, IntPtr buffer, uint bufferLength, out int bytesWritten, long offset, 152 | IDokanFileInfo info) 153 | { 154 | bytesWritten = 0; 155 | return NtStatus.AccessDenied; 156 | } 157 | 158 | public NtStatus FlushFileBuffers(string fileName, IDokanFileInfo info) 159 | { 160 | return NtStatus.NotImplemented; 161 | } 162 | 163 | public NtStatus GetFileInformation(string fileName, out FileInformation fileInfo, IDokanFileInfo info) 164 | { 165 | fileInfo = new FileInformation(); 166 | var node = this.GetNode(fileName, info); 167 | if (node == null) 168 | { 169 | return NtStatus.ObjectPathNotFound; 170 | } 171 | 172 | fileInfo.FileName = node.FullName; 173 | fileInfo.Attributes = node.IsDirectory ? FileAttributes.Directory : FileAttributes.Normal; 174 | fileInfo.CreationTime = node.CreationTime ?? this.defaultTime; 175 | fileInfo.LastAccessTime = node.LastAccessTime ?? this.defaultTime; 176 | fileInfo.LastWriteTime = node.LastWriteTime ?? this.defaultTime; 177 | fileInfo.Length = node.Length; 178 | return NtStatus.Success; 179 | } 180 | 181 | public NtStatus FindFiles(string fileName, out IList files, IDokanFileInfo info) 182 | { 183 | files = EmptyFileInformation; 184 | var node = this.GetNode(fileName, info); 185 | if (node == null) 186 | { 187 | return NtStatus.ObjectPathNotFound; 188 | } 189 | 190 | files = node.Children.Select(child => new FileInformation 191 | { 192 | FileName = child.Value.Name, 193 | Attributes = child.Value.IsDirectory ? FileAttributes.Directory : FileAttributes.Normal, 194 | CreationTime = child.Value.CreationTime ?? this.defaultTime, 195 | LastAccessTime = child.Value.LastAccessTime ?? this.defaultTime, 196 | LastWriteTime = child.Value.LastWriteTime ?? this.defaultTime, 197 | Length = child.Value.Length 198 | }).ToList(); 199 | return NtStatus.Success; 200 | } 201 | 202 | public NtStatus FindFilesWithPattern(string fileName, string searchPattern, out IList files, 203 | IDokanFileInfo info) 204 | { 205 | files = EmptyFileInformation; 206 | return NtStatus.NotImplemented; 207 | } 208 | 209 | public NtStatus SetFileAttributes(string fileName, FileAttributes attributes, IDokanFileInfo info) 210 | { 211 | return NtStatus.AccessDenied; 212 | } 213 | 214 | public NtStatus SetFileTime(string fileName, DateTime? creationTime, DateTime? lastAccessTime, 215 | DateTime? lastWriteTime, IDokanFileInfo info) 216 | { 217 | return NtStatus.AccessDenied; 218 | } 219 | 220 | public NtStatus DeleteFile(string fileName, IDokanFileInfo info) 221 | { 222 | return NtStatus.AccessDenied; 223 | } 224 | 225 | public NtStatus DeleteDirectory(string fileName, IDokanFileInfo info) 226 | { 227 | return NtStatus.AccessDenied; 228 | } 229 | 230 | public NtStatus MoveFile(string oldName, string newName, bool replace, IDokanFileInfo info) 231 | { 232 | return NtStatus.AccessDenied; 233 | } 234 | 235 | public NtStatus SetEndOfFile(string fileName, long length, IDokanFileInfo info) 236 | { 237 | return NtStatus.AccessDenied; 238 | } 239 | 240 | public NtStatus SetAllocationSize(string fileName, long length, IDokanFileInfo info) 241 | { 242 | return NtStatus.AccessDenied; 243 | } 244 | 245 | public NtStatus LockFile(string fileName, long offset, long length, IDokanFileInfo info) 246 | { 247 | return NtStatus.AccessDenied; 248 | } 249 | 250 | public NtStatus UnlockFile(string fileName, long offset, long length, IDokanFileInfo info) 251 | { 252 | return NtStatus.AccessDenied; 253 | } 254 | 255 | public NtStatus GetDiskFreeSpace(out long freeBytesAvailable, out long totalNumberOfBytes, 256 | out long totalNumberOfFreeBytes, IDokanFileInfo info) 257 | { 258 | freeBytesAvailable = totalNumberOfFreeBytes = totalNumberOfBytes = this.fsTree.Root.Length; 259 | return NtStatus.Success; 260 | } 261 | 262 | public NtStatus GetVolumeInformation(out string volumeLabel, out FileSystemFeatures features, 263 | out string fileSystemName, out uint maximumComponentLength, IDokanFileInfo info) 264 | { 265 | volumeLabel = "AVFS"; 266 | fileSystemName = "exFAT"; 267 | features = FileSystemFeatures.CasePreservedNames | FileSystemFeatures.UnicodeOnDisk | 268 | FileSystemFeatures.VolumeIsCompressed | FileSystemFeatures.ReadOnlyVolume; 269 | maximumComponentLength = 260; 270 | return NtStatus.Success; 271 | } 272 | 273 | public NtStatus GetFileSecurity(string fileName, out FileSystemSecurity security, 274 | AccessControlSections sections, IDokanFileInfo info) 275 | { 276 | security = null; 277 | return NtStatus.NotImplemented; 278 | } 279 | 280 | public NtStatus SetFileSecurity(string fileName, FileSystemSecurity security, AccessControlSections sections, 281 | IDokanFileInfo info) 282 | { 283 | return NtStatus.AccessDenied; 284 | } 285 | 286 | public NtStatus Mounted(IDokanFileInfo info) 287 | { 288 | Console.WriteLine($"Mounted readonly filesystem"); 289 | return NtStatus.Success; 290 | } 291 | 292 | public NtStatus Unmounted(IDokanFileInfo info) 293 | { 294 | Console.WriteLine($"Unmounted readonly filesystem"); 295 | return NtStatus.Success; 296 | } 297 | 298 | public NtStatus FindStreams(string fileName, out IList streams, IDokanFileInfo info) 299 | { 300 | streams = EmptyFileInformation; 301 | return NtStatus.NotImplemented; 302 | } 303 | 304 | #endregion 305 | } 306 | } -------------------------------------------------------------------------------- /WinAVFS.Core/SevenZipProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Runtime.InteropServices; 4 | using SevenZip; 5 | 6 | namespace WinAVFS.Core 7 | { 8 | public class SevenZipProvider : IArchiveProvider 9 | { 10 | private readonly ConcurrentObjectPool extractorPool; 11 | 12 | public SevenZipProvider(string path) 13 | { 14 | Console.WriteLine($"Loading archive {path} with 7z.dll"); 15 | this.extractorPool = new ConcurrentObjectPool(() => new SevenZipExtractor(path)); 16 | } 17 | 18 | public void Dispose() 19 | { 20 | foreach (var archive in this.extractorPool.GetAll()) 21 | { 22 | archive.Dispose(); 23 | } 24 | } 25 | 26 | public FSTree ReadFSTree() 27 | { 28 | var extractor = this.extractorPool.Get(); 29 | var root = new FSTreeNode(true); 30 | foreach (var entry in extractor.ArchiveFileData) 31 | { 32 | // Console.WriteLine($"Loading {entry.FileName} into FS tree"); 33 | var paths = entry.FileName.Split('/', '\\'); 34 | var node = root; 35 | for (var i = 0; i < paths.Length - 1; i++) 36 | { 37 | node = node.GetOrAddChild(true, paths[i]); 38 | } 39 | 40 | if (!string.IsNullOrEmpty(paths[paths.Length - 1])) 41 | { 42 | node = node.GetOrAddChild(entry.IsDirectory, paths[paths.Length - 1], (long) entry.Size, 43 | (long) entry.Size, entry.Index); 44 | node.CreationTime = entry.CreationTime; 45 | node.LastAccessTime = entry.LastAccessTime; 46 | node.LastWriteTime = entry.LastWriteTime; 47 | // if (!node.IsDirectory && node.Buffer == IntPtr.Zero) 48 | // { 49 | // node.Buffer = Marshal.AllocHGlobal((IntPtr) node.Length); 50 | // } 51 | } 52 | } 53 | 54 | Console.WriteLine($"Loaded {extractor.FilesCount} entries from archive"); 55 | this.extractorPool.Put(extractor); 56 | return new FSTree {Root = root}; 57 | } 58 | 59 | public void ExtractFileUnmanaged(FSTreeNode node, IntPtr buffer) 60 | { 61 | if (!(node.Context is int index)) 62 | { 63 | throw new ArgumentException(); 64 | } 65 | 66 | unsafe 67 | { 68 | using var target = new UnmanagedMemoryStream((byte*) buffer.ToPointer(), node.Length, node.Length, 69 | FileAccess.Write); 70 | var extractor = this.extractorPool.Get(); 71 | extractor.ExtractFile(index, target); 72 | this.extractorPool.Put(extractor); 73 | } 74 | } 75 | } 76 | } -------------------------------------------------------------------------------- /WinAVFS.Core/WinAVFS.Core.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net46 5 | default 6 | true 7 | Release;Debug 8 | x64 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /WinAVFS.Core/ZipArchiveProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.IO.Compression; 4 | using System.Text; 5 | 6 | namespace WinAVFS.Core 7 | { 8 | public class ZipArchiveProvider : IArchiveProvider 9 | { 10 | private readonly ZipArchive archive; 11 | 12 | public ZipArchiveProvider(string path) 13 | { 14 | Console.WriteLine($"Opening archive {path}"); 15 | this.archive = new ZipArchive(new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read), 16 | ZipArchiveMode.Read, false, Encoding.Default); 17 | } 18 | 19 | public void Dispose() 20 | { 21 | this.archive?.Dispose(); 22 | } 23 | 24 | public FSTree ReadFSTree() 25 | { 26 | var root = new FSTreeNode(true); 27 | foreach (var entry in archive.Entries) 28 | { 29 | Console.WriteLine($"Loading {entry.FullName} into FS tree"); 30 | var paths = entry.FullName.Split('/', '\\'); 31 | var node = root; 32 | for (var i = 0; i < paths.Length - 1; i++) 33 | { 34 | node = node.GetOrAddChild(true, paths[i]); 35 | } 36 | 37 | var name = paths[paths.Length - 1]; 38 | if (!string.IsNullOrEmpty(name)) 39 | { 40 | node = node.GetOrAddChild(false, name, entry.Length, entry.CompressedLength, entry); 41 | node.LastWriteTime = entry.LastWriteTime.DateTime; 42 | } 43 | 44 | node.Context = entry; 45 | } 46 | 47 | Console.WriteLine($"Loaded {archive.Entries.Count} entries from archive"); 48 | return new FSTree {Root = root}; 49 | } 50 | 51 | public void ExtractFileUnmanaged(FSTreeNode node, IntPtr buffer) 52 | { 53 | if (!(node.Context is ZipArchiveEntry entry)) 54 | { 55 | throw new ArgumentException(); 56 | } 57 | 58 | unsafe 59 | { 60 | using var source = entry.Open(); 61 | using var target = new UnmanagedMemoryStream((byte*) buffer.ToPointer(), node.Length, node.Length, 62 | FileAccess.Write); 63 | source.CopyTo(target); 64 | } 65 | } 66 | } 67 | } -------------------------------------------------------------------------------- /WinAVFS.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WinAVFS.Core", "WinAVFS.Core\WinAVFS.Core.csproj", "{0FEB6B9C-FD58-4F32-B704-F1A85F967340}" 4 | EndProject 5 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WinAVFS.CLI", "WinAVFS.CLI\WinAVFS.CLI.csproj", "{0769F9F7-0BC5-4A7C-A8DC-BAD02D4C8E6B}" 6 | EndProject 7 | Global 8 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 9 | Release|x64 = Release|x64 10 | Debug|x64 = Debug|x64 11 | EndGlobalSection 12 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 13 | {0769F9F7-0BC5-4A7C-A8DC-BAD02D4C8E6B}.Release|x64.ActiveCfg = Release|x64 14 | {0769F9F7-0BC5-4A7C-A8DC-BAD02D4C8E6B}.Release|x64.Build.0 = Release|x64 15 | {0769F9F7-0BC5-4A7C-A8DC-BAD02D4C8E6B}.Debug|x64.ActiveCfg = Debug|x64 16 | {0769F9F7-0BC5-4A7C-A8DC-BAD02D4C8E6B}.Debug|x64.Build.0 = Debug|x64 17 | {0FEB6B9C-FD58-4F32-B704-F1A85F967340}.Release|x64.ActiveCfg = Release|x64 18 | {0FEB6B9C-FD58-4F32-B704-F1A85F967340}.Release|x64.Build.0 = Release|x64 19 | {0FEB6B9C-FD58-4F32-B704-F1A85F967340}.Debug|x64.ActiveCfg = Debug|x64 20 | {0FEB6B9C-FD58-4F32-B704-F1A85F967340}.Debug|x64.Build.0 = Debug|x64 21 | EndGlobalSection 22 | EndGlobal 23 | --------------------------------------------------------------------------------