using SMBLibrary; using SmbSvr.Ramfs.Inters; using System; using System.Collections.Generic; using System.IO; using System.Linq; using Utilities; namespace SmbSvr.Ramfs { internal class RamfsFileSystem : FileSystem { private readonly RamfsDirEntry _root = new RamfsDirEntry(); public RamfsFileSystem() { _root.Entries.Add(new RamfsDirEntry { Name = "temp", ParentDir = _root }); } public override IFileSystemEntry GetEntry(string path) { if (path == "\\") return _root; var seg = path.Split(new[] { '\\' }, StringSplitOptions.RemoveEmptyEntries); RamfsFileSystemEntry ptr = _root; foreach (var s in seg) { if (!ptr.IsDirectory) { ptr = null; break; } ptr = ((RamfsDirEntry)ptr).Entries.FirstOrDefault(p => p.Name == s); if (ptr == null) break; } return ptr; } private RamfsFileSystemEntry GetFsEntry(string path) => GetEntry(path) as RamfsFileSystemEntry ?? throw new FileNotFoundException(); private RamfsDirEntry GetDirEntry(string path) => GetEntry(path) as RamfsDirEntry ?? throw new DirectoryNotFoundException(); private RamfsFileEntry GetFileEntry(string path) => GetEntry(path) as RamfsFileEntry ?? throw new FileNotFoundException(); public override List ListEntriesInDirectory(string path) => GetDirEntry(path).Entries.Cast().ToList(); public override IFileSystemEntry CreateDirectory(string path) { var dir = GetDirEntry(Path.GetDirectoryName(path)); var n = Path.GetFileName(path); if (dir.Entries.Any(p => p.Name == n)) throw new IOException("", (int)Win32Error.ERROR_ALREADY_EXISTS); var nd = new RamfsDirEntry { Name = n, ParentDir = dir }; dir.Entries.Add(nd); return nd; } public override IFileSystemEntry CreateFile(string path) { var dir = GetDirEntry(Path.GetDirectoryName(path)); var n = Path.GetFileName(path); if (dir.Entries.Any(p => p.Name == n)) throw new IOException("", (int)Win32Error.ERROR_ALREADY_EXISTS); var nf = new RamfsFileEntry { Name = n, ParentDir = dir }; dir.Entries.Add(nf); return nf; } public override void Move(string source, string destination) { var destEntry = GetEntry(destination); if (destEntry != null) throw new IOException("", (int)Win32Error.ERROR_ALREADY_EXISTS); var destDir = GetDirEntry(Path.GetDirectoryName(destination)); var nn = Path.GetFileName(destination); if (destDir.Entries.Any(p => p.Name == nn)) throw new IOException("", (int)Win32Error.ERROR_ALREADY_EXISTS); var sourceEntry = GetFsEntry(source); //remove from source dir //change name //add to dest dir sourceEntry.ParentDir.Entries.Remove(sourceEntry); sourceEntry.Name = nn; sourceEntry.ParentDir = destDir; destDir.Entries.Add(sourceEntry); } public override void Delete(string path) { var entry = GetFsEntry(path); entry.ParentDir.Entries.Remove(entry); entry.Dispose(); } public override Stream OpenFile(string path, FileMode mode, FileAccess access, FileShare share, FileOptions options) { var file = GetFileEntry(path); var s = new StreamWrap(file.MemoryStream); if (mode == FileMode.Append) s.Position = s.Length; //if (options.HasFlag(FileOptions.DeleteOnClose)) s.Closed += delegate { Delete(path); }; return s; } public override void SetAttributes(string path, bool? isHidden, bool? isReadonly, bool? isArchived) { var entry = GetFsEntry(path); if (isHidden.HasValue) entry.IsHidden = isHidden.Value; if (isReadonly.HasValue) entry.IsHidden = isReadonly.Value; if (isArchived.HasValue) entry.IsHidden = isArchived.Value; } public override void SetDates(string path, DateTime? creationDT, DateTime? lastWriteDT, DateTime? lastAccessDT) { var entry = GetFsEntry(path); if (creationDT.HasValue) entry.CreationTime = creationDT.Value; if (lastWriteDT.HasValue) entry.CreationTime = lastWriteDT.Value; if (lastAccessDT.HasValue) entry.CreationTime = lastAccessDT.Value; } public override string Name => "RAMFS"; public override long Size => long.MaxValue; public override long FreeSpace => long.MaxValue; } internal abstract class RamfsFileSystemEntry : FileSystemEntry, IDisposable { protected RamfsFileSystemEntry() { CreationTime = LastAccessTime = LastWriteTime = DateTime.Now; } public RamfsDirEntry ParentDir { get; set; } public abstract void Dispose(); } internal class RamfsFileEntry : RamfsFileSystemEntry { public override bool IsDirectory => false; public MemoryStream MemoryStream { get; set; } = new MemoryStream(); public override ulong Size => (ulong)MemoryStream.Length; public override void Dispose() { MemoryStream.Dispose(); } } internal class RamfsDirEntry : RamfsFileSystemEntry { public override bool IsDirectory => true; public List Entries { get; } = new List(); public override ulong Size => (ulong)Entries.Sum(p => (long)p.Size); public override void Dispose() { foreach (var entry in Entries.ToArray()) { Entries.Remove(entry); entry.Dispose(); } } } }