/* Tesses.VirtualFilesystem a library for virtual filesystems in .NET Copyright (C) 2023 Mike Nolan This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ using Zio; using Zio.FileSystems; using Tesses.VirtualFilesystem; using System.Collections.Generic; using System; using System.IO; using Tesses.VirtualFilesystem.Extensions; namespace Tesses.VirtualFilesystem.Filesystems { public class ZioFileSystem : SyncFileSystem { IFileSystem fs; public IFileSystem Filesystem => fs; public override void Dispose() { fs.Dispose(); } public ZioFileSystem(IFileSystem fs) { this.fs = fs; } public UPath ConvertUPathFromUnixPath(UnixPath path) { return path.Path; } public UnixPath ConvertUPathToUnixPath(UPath path) { return new UnixPath(path.FullName); } public override void CreateDirectory(UnixPath directory) { fs.CreateDirectory(ConvertUPathFromUnixPath(directory)); } public override void DeleteDirectory(UnixPath path) { fs.DeleteDirectory(ConvertUPathFromUnixPath(path),false); } public override void DeleteFile(UnixPath path) { fs.DeleteFile(ConvertUPathFromUnixPath(path)); } public override bool DirectoryExists(UnixPath path) { return fs.DirectoryExists(ConvertUPathFromUnixPath(path)); } public override bool FileExists(UnixPath path) { return fs.FileExists(ConvertUPathFromUnixPath(path)); } public override DateTime GetCreationTime(UnixPath path) { return fs.GetCreationTime(ConvertUPathFromUnixPath(path)); } public override DateTime GetLastAccessTime(UnixPath path) { return fs.GetLastAccessTime(ConvertUPathFromUnixPath(path)); } public override DateTime GetLastWriteTime(UnixPath path) { return fs.GetLastWriteTime(ConvertUPathFromUnixPath(path)); } public override void MoveDirectory(UnixPath src, UnixPath dest) { fs.MoveDirectory(ConvertUPathFromUnixPath(src),ConvertUPathFromUnixPath(dest)); } public override void MoveFile(UnixPath src, UnixPath dest) { fs.MoveFile(ConvertUPathFromUnixPath(src),ConvertUPathFromUnixPath(dest)); } public override Stream Open(UnixPath path, FileMode mode, FileAccess access, FileShare share) { return fs.OpenFile(ConvertUPathFromUnixPath(path),mode,access,share); } public override void SetCreationTime(UnixPath path, DateTime time) { fs.SetCreationTime(ConvertUPathFromUnixPath(path),time); } public override void SetLastAccessTime(UnixPath path, DateTime time) { fs.SetLastAccessTime(ConvertUPathFromUnixPath(path),time); } public override void SetLastWriteTime(UnixPath path, DateTime time) { fs.SetLastWriteTime(ConvertUPathFromUnixPath(path),time); } public override void SetAttributes(UnixPath path, FileAttributes attributes) { fs.SetAttributes(ConvertUPathFromUnixPath(path),attributes); } public override FileAttributes GetAttributes(UnixPath path) { return fs.GetAttributes(ConvertUPathFromUnixPath(path)); } public override bool CanWatch(UnixPath path) { return fs.CanWatch(ConvertUPathFromUnixPath(path)); } public override IVirtualWatcher WatchDirectory(UnixPath dir) { return new ZioWatcher(this,fs,dir); } public override IEnumerable EnumerateFileSystemEntries(UnixPath path) { foreach(var item in fs.EnumeratePaths(ConvertUPathFromUnixPath(path))) { yield return ConvertUPathToUnixPath(item); } } private class ZioWatcher : IVirtualWatcher { Zio.IFileSystemWatcher watcher; ZioFileSystem _fs; public ZioWatcher(ZioFileSystem fs,IFileSystem fs2,UnixPath path) { watcher = fs2.Watch(fs.ConvertUPathFromUnixPath(path)); watcher.Changed += (sender,e)=>{ Changed?.Invoke(this,new VirtualWatcherChangedArgs(fs,(System.IO.WatcherChangeTypes)(int)e.ChangeType,fs.ConvertUPathToUnixPath(e.FullPath))); }; watcher.Created += (sender,e)=>{ Created?.Invoke(this,new VirtualWatcherChangedArgs(fs,(System.IO.WatcherChangeTypes)(int)e.ChangeType,fs.ConvertUPathToUnixPath(e.FullPath))); }; watcher.Deleted += (sender,e)=>{ Deleted?.Invoke(this,new VirtualWatcherChangedArgs(fs,(System.IO.WatcherChangeTypes)(int)e.ChangeType,fs.ConvertUPathToUnixPath(e.FullPath))); }; watcher.Error += (sender,e)=>{ Error?.Invoke(this,new ErrorEventArgs(e.Exception)); }; watcher.Renamed += (sender,e)=>{ Renamed?.Invoke(this,new VirtualWatcherRenamedEventArgs(fs,(System.IO.WatcherChangeTypes)(int)e.ChangeType,fs.ConvertUPathToUnixPath(e.FullPath),fs.ConvertUPathToUnixPath(e.OldFullPath))); }; _fs=fs; } public int InternalBufferSize { get => watcher.InternalBufferSize; set => watcher.InternalBufferSize=value;} public System.IO.NotifyFilters NotifyFilter { get => (System.IO.NotifyFilters)watcher.NotifyFilter; set => watcher.NotifyFilter=(Zio.NotifyFilters)value; } public bool EnableRaisingEvents { get => watcher.EnableRaisingEvents; set => watcher.EnableRaisingEvents=value; } public string Filter { get => watcher.Filter; set => watcher.Filter=value; } public bool IncludeSubdirectories { get => watcher.IncludeSubdirectories; set => watcher.IncludeSubdirectories=value;} public event EventHandler Changed; public event EventHandler Created; public event EventHandler Deleted; public event EventHandler Renamed; public event EventHandler Error; public void Dispose() { this.watcher.Dispose(); } } } public class ZioMountableWrapper : ZioFileSystem { public MountFileSystem MountFilesystem {get;} private ZioMountableWrapper(MountFileSystem fs) : base(fs) { MountFilesystem = fs; } public static ZioMountableWrapper Create(IVirtualFilesystem fs,bool owned=true) { return new ZioMountableWrapper(new MountFileSystem(new TessesVFSFilesystem(fs),owned)); } public static ZioMountableWrapper Create(bool owned=true) { return new ZioMountableWrapper(new MountFileSystem(owned)); } public void Mount(UnixPath path,IVirtualFilesystem fs) { MountFilesystem.Mount(path.Path,new TessesVFSFilesystem(fs)); } public bool IsMounted(UnixPath path) { return MountFilesystem.IsMounted(path.Path); } public Dictionary GetMounts() { Dictionary fs=new Dictionary(); foreach(var item in MountFilesystem.GetMounts()) { var _fs=item.Value as TessesVFSFilesystem; if(_fs !=null) fs.Add(item.Key.FullName,_fs.Filesystem); } return fs; } } } namespace Zio.FileSystems { public class TessesVFSFilesystem : Zio.FileSystems.FileSystem { protected override void Dispose(bool disposing) { if(disposing) fs.Dispose(); } IVirtualFilesystem fs; public IVirtualFilesystem Filesystem => fs; public TessesVFSFilesystem(IVirtualFilesystem fs) { this.fs = fs; } protected override UPath ConvertPathFromInternalImpl(string innerPath) { return innerPath; } protected override string ConvertPathToInternalImpl(UPath path) { return path.FullName; } protected override void CopyFileImpl(UPath srcPath, UPath destPath, bool overwrite) { if(!overwrite && fs.FileExists(fs.ConvertPathToUnixPath(ConvertPathToInternal(destPath)))) throw new IOException("File already exists"); fs.CopyFile(fs.ConvertPathToUnixPath(ConvertPathToInternal(srcPath)),fs.ConvertPathToUnixPath(ConvertPathToInternal(destPath))); } protected override void CreateDirectoryImpl(UPath path) { fs.CreateDirectory(fs.ConvertPathToUnixPath(ConvertPathToInternal(path))); } protected override void DeleteDirectoryImpl(UPath path, bool isRecursive) { fs.DeleteDirectory(fs.ConvertPathToUnixPath(ConvertPathToInternal(path)),isRecursive); } protected override void DeleteFileImpl(UPath path) { fs.DeleteFile(fs.ConvertPathToUnixPath(ConvertPathToInternal(path))); } protected override bool DirectoryExistsImpl(UPath path) { return fs.DirectoryExists(fs.ConvertPathToUnixPath(ConvertPathToInternal(path))); } protected override IEnumerable EnumerateItemsImpl(UPath path, SearchOption searchOption, SearchPredicate searchPredicate) { foreach(var item in fs.EnumerateFileSystemEntries(fs.ConvertPathToUnixPath(ConvertPathToInternal(path)))) { yield return new FileSystemItem(this,ConvertPathFromInternal(fs.ConvertPathFromUnixPath(item)),fs.DirectoryExists(item)); } } protected override IEnumerable EnumeratePathsImpl(UPath path, string searchPattern, SearchOption searchOption, SearchTarget searchTarget) { foreach(var item in fs.EnumerateFileSystemEntries(fs.ConvertPathToUnixPath(ConvertPathToInternal(path)))) { yield return ConvertPathFromInternal(fs.ConvertPathFromUnixPath(item)); } } protected override bool FileExistsImpl(UPath path) { return fs.FileExists(fs.ConvertPathToUnixPath(ConvertPathToInternal(path))); } protected override FileAttributes GetAttributesImpl(UPath path) { return fs.GetAttributes(fs.ConvertPathToUnixPath(ConvertPathToInternal(path))); } protected override DateTime GetCreationTimeImpl(UPath path) { return fs.GetCreationTime(fs.ConvertPathToUnixPath(ConvertPathToInternal(path))); } protected override long GetFileLengthImpl(UPath path) { using(var f = fs.OpenRead(fs.ConvertPathToUnixPath(ConvertPathToInternal(path)))) { return f.Length; } } protected override DateTime GetLastAccessTimeImpl(UPath path) { return fs.GetLastAccessTime(fs.ConvertPathToUnixPath(ConvertPathToInternal(path))); } protected override DateTime GetLastWriteTimeImpl(UPath path) { return fs.GetLastWriteTime(fs.ConvertPathToUnixPath(ConvertPathToInternal(path))); } protected override void MoveDirectoryImpl(UPath srcPath, UPath destPath) { fs.MoveDirectory(fs.ConvertPathToUnixPath(ConvertPathToInternal(srcPath)),fs.ConvertPathToUnixPath(ConvertPathToInternal(destPath))); } protected override void MoveFileImpl(UPath srcPath, UPath destPath) { fs.MoveDirectory(fs.ConvertPathToUnixPath(ConvertPathToInternal(srcPath)),fs.ConvertPathToUnixPath(ConvertPathToInternal(destPath))); } protected override Stream OpenFileImpl(UPath path, FileMode mode, FileAccess access, FileShare share) { return fs.Open(fs.ConvertPathToUnixPath(ConvertPathToInternal(path)),mode,access,share); } protected override void ReplaceFileImpl(UPath srcPath, UPath destPath, UPath destBackupPath, bool ignoreMetadataErrors) { MoveFile(destPath,destBackupPath); MoveFile(srcPath,destPath); } protected override void SetAttributesImpl(UPath path, FileAttributes attributes) { fs.SetAttributes(fs.ConvertPathToUnixPath(ConvertPathToInternal(path)),attributes); } protected override void SetCreationTimeImpl(UPath path, DateTime time) { fs.SetCreationTime(fs.ConvertPathToUnixPath(ConvertPathToInternal(path)),time); } protected override void SetLastAccessTimeImpl(UPath path, DateTime time) { fs.SetLastAccessTime(fs.ConvertPathToUnixPath(ConvertPathToInternal(path)),time); } protected override void SetLastWriteTimeImpl(UPath path, DateTime time) { fs.SetLastWriteTime(fs.ConvertPathToUnixPath(ConvertPathToInternal(path)),time); } protected override IFileSystemWatcher WatchImpl(UPath path) { return new _FSW(this,fs,fs.ConvertPathToUnixPath(ConvertPathToInternal(path))); } protected override void CreateSymbolicLinkImpl(UPath path, UPath pathToTarget) { fs.CreateSymlink(fs.ConvertPathToUnixPath(ConvertPathToInternal(path)),fs.ConvertPathToUnixPath(ConvertPathToInternal(pathToTarget))); } protected override bool TryResolveLinkTargetImpl(UPath linkPath, out UPath resolvedPath) { if(fs.SymlinkExists(fs.ConvertPathToUnixPath(ConvertPathToInternal(linkPath)))) { resolvedPath = ConvertPathFromInternal(fs.ConvertPathFromUnixPath(fs.ReadLink(fs.ConvertPathToUnixPath(ConvertPathToInternal(linkPath))))); return true; } else { resolvedPath = ConvertPathFromInternal(fs.ConvertPathFromUnixPath(Special.Root)); return false; } } private class _FSW : IFileSystemWatcher { IVirtualWatcher watcher; public _FSW(IFileSystem fs2,IVirtualFilesystem fs,UnixPath unixPath) { this.FileSystem=fs2; watcher=fs.WatchDirectory(unixPath); Path = fs2.ConvertPathFromInternal(fs.ConvertPathFromUnixPath(unixPath)); this._fs = fs; watcher.Changed += W_Changed; watcher.Created += W_Created; watcher.Deleted += W_Deleted; watcher.Error += W_Error; watcher.Renamed += W_Renamed; } private void W_Renamed(object sender, VirtualWatcherRenamedEventArgs e) { Renamed?.Invoke(this,new FileRenamedEventArgs(FileSystem,(Zio.WatcherChangeTypes)e.ChangeType,FileSystem.ConvertPathFromInternal(_fs.ConvertPathFromUnixPath(e.FullPath)),FileSystem.ConvertPathFromInternal(_fs.ConvertPathFromUnixPath(e.OldFullPath)))); } private void W_Error(object sender, System.IO.ErrorEventArgs e) { Error?.Invoke(this,new FileSystemErrorEventArgs(e.GetException())); } private void W_Deleted(object sender, VirtualWatcherChangedArgs e) { Deleted?.Invoke(this,new FileChangedEventArgs(FileSystem,(Zio.WatcherChangeTypes)e.ChangeType,FileSystem.ConvertPathFromInternal(_fs.ConvertPathFromUnixPath(e.FullPath)))); } IVirtualFilesystem _fs; private void W_Created(object sender, VirtualWatcherChangedArgs e) { Created?.Invoke(this,new FileChangedEventArgs(FileSystem,(Zio.WatcherChangeTypes)e.ChangeType,FileSystem.ConvertPathFromInternal(_fs.ConvertPathFromUnixPath(e.FullPath)))); } private void W_Changed(object sender, VirtualWatcherChangedArgs e) { Changed?.Invoke(this,new FileChangedEventArgs(FileSystem,(Zio.WatcherChangeTypes)e.ChangeType,FileSystem.ConvertPathFromInternal(_fs.ConvertPathFromUnixPath(e.FullPath)))); } public IFileSystem FileSystem {get;} public UPath Path {get;} public int InternalBufferSize { get => watcher.InternalBufferSize; set => watcher.InternalBufferSize = value; } public Zio.NotifyFilters NotifyFilter { get => (Zio.NotifyFilters)watcher.NotifyFilter; set => watcher.NotifyFilter = (System.IO.NotifyFilters)value; } public bool EnableRaisingEvents { get => watcher.EnableRaisingEvents; set => watcher.EnableRaisingEvents=value; } public string Filter { get => watcher.Filter; set => watcher.Filter=value; } public bool IncludeSubdirectories { get => watcher.IncludeSubdirectories; set => watcher.IncludeSubdirectories=value; } public event EventHandler Changed; public event EventHandler Created; public event EventHandler Deleted; public event EventHandler Error; public event EventHandler Renamed; public void Dispose() { watcher.Changed -= W_Changed; watcher.Created -= W_Created; watcher.Deleted -= W_Deleted; watcher.Error -= W_Error; watcher.Renamed -= W_Renamed; watcher.Dispose(); } } } }