478 lines
19 KiB
C#
478 lines
19 KiB
C#
/*
|
|
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 <https://www.gnu.org/licenses/>.
|
|
*/
|
|
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.ToString());
|
|
}
|
|
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<UnixPath> 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<VirtualWatcherChangedArgs> Changed;
|
|
public event EventHandler<VirtualWatcherChangedArgs> Created;
|
|
public event EventHandler<VirtualWatcherChangedArgs> Deleted;
|
|
public event EventHandler<VirtualWatcherRenamedEventArgs> Renamed;
|
|
public event EventHandler<ErrorEventArgs> 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<UnixPath,IVirtualFilesystem> GetMounts()
|
|
{
|
|
Dictionary<UnixPath,IVirtualFilesystem> fs=new Dictionary<UnixPath, IVirtualFilesystem>();
|
|
|
|
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<FileSystemItem> 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<UPath> 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<FileChangedEventArgs> Changed;
|
|
public event EventHandler<FileChangedEventArgs> Created;
|
|
public event EventHandler<FileChangedEventArgs> Deleted;
|
|
public event EventHandler<FileSystemErrorEventArgs> Error;
|
|
public event EventHandler<FileRenamedEventArgs> 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();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
} |