2015 lines
71 KiB
C#
2015 lines
71 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 System;
|
|||
|
using System.Collections.Generic;
|
|||
|
using System.IO;
|
|||
|
using System.Runtime.CompilerServices;
|
|||
|
using System.Threading;
|
|||
|
using System.Threading.Tasks;
|
|||
|
using Tesses.VirtualFilesystem;
|
|||
|
using Tesses.VirtualFilesystem.Extensions;
|
|||
|
using System.Linq;
|
|||
|
namespace Tesses.VirtualFilesystem
|
|||
|
{
|
|||
|
public class SubdirFilesystem : IVirtualFilesystem
|
|||
|
{
|
|||
|
IVirtualFilesystem _fs;
|
|||
|
UnixPath _path;
|
|||
|
public SubdirFilesystem(IVirtualFilesystem fs,UnixPath path)
|
|||
|
{
|
|||
|
this._fs=fs;
|
|||
|
this._path=path;
|
|||
|
}
|
|||
|
|
|||
|
public bool CanHandleSymlinks(UnixPath path)
|
|||
|
{
|
|||
|
return _fs.CanHandleSymlinks(_path / path);
|
|||
|
}
|
|||
|
|
|||
|
public bool CanWatch(UnixPath path)
|
|||
|
{
|
|||
|
return _fs.CanWatch(_path / path);
|
|||
|
}
|
|||
|
|
|||
|
private UnixPath ChopOff(UnixPath path)
|
|||
|
{
|
|||
|
if(path.Parts.Length > _path.Parts.Length)
|
|||
|
{
|
|||
|
return new UnixPath(path.Parts.Skip(_path.Parts.Length));
|
|||
|
}
|
|||
|
return new UnixPath();
|
|||
|
}
|
|||
|
|
|||
|
public string ConvertPathFromUnixPath(UnixPath path)
|
|||
|
{
|
|||
|
return _fs.ConvertPathFromUnixPath(path);
|
|||
|
}
|
|||
|
|
|||
|
public UnixPath ConvertPathToUnixPath(string path)
|
|||
|
{
|
|||
|
return _fs.ConvertPathToUnixPath(path);
|
|||
|
}
|
|||
|
|
|||
|
public void CreateDirectory(UnixPath directory)
|
|||
|
{
|
|||
|
_fs.CreateDirectory(_path / directory);
|
|||
|
}
|
|||
|
|
|||
|
public async Task CreateDirectoryAsync(UnixPath directory, CancellationToken token = default)
|
|||
|
{
|
|||
|
await _fs.CreateDirectoryAsync(_path / directory,token);
|
|||
|
}
|
|||
|
|
|||
|
public void CreateHardlink(UnixPath src, UnixPath dest)
|
|||
|
{
|
|||
|
_fs.CreateHardlink(_path / src,_path / dest);
|
|||
|
}
|
|||
|
|
|||
|
public async Task CreateHardlinkAsync(UnixPath src, UnixPath dest, CancellationToken token = default)
|
|||
|
{
|
|||
|
await _fs.CreateHardlinkAsync(_path / src,_path /dest,token);
|
|||
|
}
|
|||
|
|
|||
|
public void CreateSymlink(UnixPath src, UnixPath dest)
|
|||
|
{
|
|||
|
_fs.CreateSymlink(_path / src,_path / dest);
|
|||
|
}
|
|||
|
|
|||
|
public async Task CreateSymlinkAsync(UnixPath src, UnixPath dest, CancellationToken token = default)
|
|||
|
{
|
|||
|
await _fs.CreateSymlinkAsync(_path / src,_path / dest,token);
|
|||
|
}
|
|||
|
|
|||
|
public void DeleteDirectory(UnixPath path)
|
|||
|
{
|
|||
|
_fs.DeleteDirectory(_path / path);
|
|||
|
}
|
|||
|
|
|||
|
public async Task DeleteDirectoryAsync(UnixPath path, CancellationToken token = default)
|
|||
|
{
|
|||
|
await _fs.DeleteDirectoryAsync(_path / path,token);
|
|||
|
}
|
|||
|
|
|||
|
public void DeleteFile(UnixPath path)
|
|||
|
{
|
|||
|
_fs.DeleteFile(_path / path);
|
|||
|
}
|
|||
|
|
|||
|
public async Task DeleteFileAsync(UnixPath path, CancellationToken token = default)
|
|||
|
{
|
|||
|
await _fs.DeleteFileAsync(_path / path,token);
|
|||
|
}
|
|||
|
|
|||
|
public bool DirectoryExists(UnixPath path)
|
|||
|
{
|
|||
|
return _fs.DirectoryExists(_path / path);
|
|||
|
}
|
|||
|
|
|||
|
public async Task<bool> DirectoryExistsAsync(UnixPath path, CancellationToken token = default)
|
|||
|
{
|
|||
|
return await _fs.DirectoryExistsAsync(_path / path,token);
|
|||
|
}
|
|||
|
|
|||
|
public void Dispose()
|
|||
|
{
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
public IEnumerable<UnixPath> EnumerateDirectories(UnixPath path)
|
|||
|
{
|
|||
|
foreach(var dir in _fs.EnumerateDirectories(_path / path))
|
|||
|
{
|
|||
|
yield return ChopOff(dir);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public async IAsyncEnumerable<UnixPath> EnumerateDirectoriesAsync(UnixPath path, [EnumeratorCancellation]CancellationToken token = default)
|
|||
|
{
|
|||
|
await foreach(var dir in _fs.EnumerateDirectoriesAsync(_path / path,token))
|
|||
|
{
|
|||
|
if(token.IsCancellationRequested) yield break;
|
|||
|
yield return ChopOff(dir);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public IEnumerable<UnixPath> EnumerateFiles(UnixPath path)
|
|||
|
{
|
|||
|
foreach(var file in _fs.EnumerateFiles(_path / path))
|
|||
|
{
|
|||
|
|
|||
|
yield return ChopOff(file);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public async IAsyncEnumerable<UnixPath> EnumerateFilesAsync(UnixPath path, [EnumeratorCancellation] CancellationToken token = default)
|
|||
|
{
|
|||
|
await foreach(var file in _fs.EnumerateFilesAsync(_path / path,token))
|
|||
|
{
|
|||
|
if(token.IsCancellationRequested) yield break;
|
|||
|
yield return ChopOff(file);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public bool FileExists(UnixPath path)
|
|||
|
{
|
|||
|
return _fs.FileExists(_path / path);
|
|||
|
}
|
|||
|
|
|||
|
public async Task<bool> FileExistsAsync(UnixPath path, CancellationToken token = default)
|
|||
|
{
|
|||
|
return await _fs.FileExistsAsync(_path / path,token);
|
|||
|
}
|
|||
|
|
|||
|
public FileAttributes GetAttributes(UnixPath path)
|
|||
|
{
|
|||
|
return _fs.GetAttributes(_path / path);
|
|||
|
}
|
|||
|
|
|||
|
public async Task<FileAttributes> GetAttributesAsync(UnixPath path, CancellationToken token = default)
|
|||
|
{
|
|||
|
return await _fs.GetAttributesAsync(_path / path,token);
|
|||
|
}
|
|||
|
|
|||
|
public DateTime GetCreationTime(UnixPath path)
|
|||
|
{
|
|||
|
return _fs.GetCreationTime(_path / path);
|
|||
|
}
|
|||
|
|
|||
|
public async Task<DateTime> GetCreationTimeAsync(UnixPath path, CancellationToken token = default)
|
|||
|
{
|
|||
|
return await _fs.GetCreationTimeAsync(_path / path,token);
|
|||
|
}
|
|||
|
|
|||
|
public DateTime GetLastAccessTime(UnixPath path)
|
|||
|
{
|
|||
|
return _fs.GetLastAccessTime(_path / path);
|
|||
|
}
|
|||
|
|
|||
|
public async Task<DateTime> GetLastAccessTimeAsync(UnixPath path, CancellationToken token = default)
|
|||
|
{
|
|||
|
return await _fs.GetLastAccessTimeAsync(_path / path,token);
|
|||
|
}
|
|||
|
|
|||
|
public DateTime GetLastWriteTime(UnixPath path)
|
|||
|
{
|
|||
|
return _fs.GetLastWriteTime(_path / path);
|
|||
|
}
|
|||
|
|
|||
|
public async Task<DateTime> GetLastWriteTimeAsync(UnixPath path, CancellationToken token = default)
|
|||
|
{
|
|||
|
return await _fs.GetLastWriteTimeAsync(_path / path,token);
|
|||
|
}
|
|||
|
|
|||
|
public IVirtualFilesystem GetSubdirFilesystem(UnixPath path)
|
|||
|
{
|
|||
|
return new SubdirFilesystem(this,path);
|
|||
|
}
|
|||
|
|
|||
|
public void MoveDirectory(UnixPath src, UnixPath dest)
|
|||
|
{
|
|||
|
_fs.MoveDirectory(_path / src,_path / dest);
|
|||
|
}
|
|||
|
|
|||
|
public async Task MoveDirectoryAsync(UnixPath src, UnixPath dest)
|
|||
|
{
|
|||
|
await _fs.MoveDirectoryAsync(_path / src, _path / dest);
|
|||
|
}
|
|||
|
|
|||
|
public void MoveFile(UnixPath src, UnixPath dest)
|
|||
|
{
|
|||
|
_fs.MoveFile(_path / src,_path / dest);
|
|||
|
}
|
|||
|
|
|||
|
public async Task MoveFileAsync(UnixPath src, UnixPath dest)
|
|||
|
{
|
|||
|
await _fs.MoveFileAsync(_path / src, _path / dest);
|
|||
|
}
|
|||
|
|
|||
|
public Stream Open(UnixPath path, FileMode mode, FileAccess access, FileShare share)
|
|||
|
{
|
|||
|
return _fs.Open(_path / path,mode,access,share);
|
|||
|
}
|
|||
|
|
|||
|
public async Task<Stream> OpenAsync(UnixPath path, FileMode mode, FileAccess access, FileShare share, CancellationToken token = default)
|
|||
|
{
|
|||
|
return await _fs.OpenAsync(_path / path,mode,access,share,token);
|
|||
|
}
|
|||
|
|
|||
|
public DirectoryPointer OpenDirectory()
|
|||
|
{
|
|||
|
return new DirectoryPointer(this);
|
|||
|
}
|
|||
|
|
|||
|
public DirectoryPointer OpenDirectory(UnixPath path)
|
|||
|
{
|
|||
|
return new DirectoryPointer(this,path);
|
|||
|
}
|
|||
|
|
|||
|
public UnixPath ReadLink(UnixPath file)
|
|||
|
{
|
|||
|
return _fs.ReadLink(_path / file);
|
|||
|
}
|
|||
|
|
|||
|
public async Task<UnixPath> ReadLinkAsync(UnixPath file, CancellationToken token = default)
|
|||
|
{
|
|||
|
return await _fs.ReadLinkAsync(_path / file,token);
|
|||
|
}
|
|||
|
|
|||
|
public bool SameFileSystem(UnixPath src, UnixPath dest)
|
|||
|
{
|
|||
|
return _fs.SameFileSystem(_path / src,_path / dest);
|
|||
|
}
|
|||
|
|
|||
|
public async Task<bool> SameFileSystemAsync(UnixPath src, UnixPath dest, CancellationToken token = default)
|
|||
|
{
|
|||
|
return await _fs.SameFileSystemAsync(_path / src, _path / dest,token);
|
|||
|
}
|
|||
|
|
|||
|
public void SetAttributes(UnixPath path, FileAttributes attributes)
|
|||
|
{
|
|||
|
_fs.SetAttributes(_path / path,attributes);
|
|||
|
}
|
|||
|
|
|||
|
public async Task SetAttributesAsync(UnixPath path, FileAttributes attributes, CancellationToken token = default)
|
|||
|
{
|
|||
|
await _fs.SetAttributesAsync(_path / path,attributes,token);
|
|||
|
}
|
|||
|
|
|||
|
public void SetCreationTime(UnixPath path, DateTime time)
|
|||
|
{
|
|||
|
_fs.SetCreationTime(_path / path,time);
|
|||
|
}
|
|||
|
|
|||
|
public async Task SetCreationTimeAsync(UnixPath path, DateTime time, CancellationToken token = default)
|
|||
|
{
|
|||
|
await _fs.SetCreationTimeAsync(_path / path,time,token);
|
|||
|
}
|
|||
|
|
|||
|
public void SetLastAccessTime(UnixPath path, DateTime time)
|
|||
|
{
|
|||
|
_fs.SetLastAccessTime(_path / path,time);
|
|||
|
}
|
|||
|
|
|||
|
public async Task SetLastAccessTimeAsync(UnixPath path, DateTime time, CancellationToken token = default)
|
|||
|
{
|
|||
|
await _fs.SetLastAccessTimeAsync(_path / path,time,token);
|
|||
|
}
|
|||
|
|
|||
|
public void SetLastWriteTime(UnixPath path, DateTime time)
|
|||
|
{
|
|||
|
_fs.SetLastWriteTime(_path / path,time);
|
|||
|
}
|
|||
|
|
|||
|
public async Task SetLastWriteTimeAsync(UnixPath path, DateTime time, CancellationToken token = default)
|
|||
|
{
|
|||
|
await _fs.SetLastWriteTimeAsync(_path / path,time,token);
|
|||
|
}
|
|||
|
|
|||
|
public bool SymlinkExists(UnixPath file)
|
|||
|
{
|
|||
|
return _fs.SymlinkExists(_path / file);
|
|||
|
}
|
|||
|
|
|||
|
public async Task<bool> SymlinkExistsAsync(UnixPath file, CancellationToken token = default)
|
|||
|
{
|
|||
|
return await _fs.SymlinkExistsAsync(_path / file,token);
|
|||
|
}
|
|||
|
|
|||
|
public UnixPath UpPath(UnixPath path)
|
|||
|
{
|
|||
|
return path.Parent;
|
|||
|
}
|
|||
|
|
|||
|
public IVirtualWatcher WatchDirectory(UnixPath dir)
|
|||
|
{
|
|||
|
return _fs.WatchDirectory(_path / dir);
|
|||
|
}
|
|||
|
|
|||
|
public EntryPointer OpenEntry(UnixPath path)
|
|||
|
{
|
|||
|
return _fs.OpenEntry(_path / path);
|
|||
|
}
|
|||
|
|
|||
|
public FilePointer OpenFile(UnixPath path)
|
|||
|
{
|
|||
|
return _fs.OpenFile(path);
|
|||
|
}
|
|||
|
|
|||
|
public SymlinkPointer OpenSymlink(UnixPath path)
|
|||
|
{
|
|||
|
return _fs.OpenSymlink(path);
|
|||
|
}
|
|||
|
|
|||
|
public IEnumerable<UnixPath> EnumerateFileSystemEntries(UnixPath path)
|
|||
|
{
|
|||
|
foreach(var item in _fs.EnumerateFileSystemEntries(_path/path))
|
|||
|
{
|
|||
|
yield return ChopOff(item);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public IEnumerable<UnixPath> EnumerateSymlinks(UnixPath path)
|
|||
|
{
|
|||
|
foreach(var item in _fs.EnumerateSymlinks(_path/path))
|
|||
|
{
|
|||
|
yield return ChopOff(item);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public async IAsyncEnumerable<UnixPath> EnumerateSymlinksAsync(UnixPath path, [EnumeratorCancellation]CancellationToken token = default)
|
|||
|
{
|
|||
|
await foreach(var item in _fs.EnumerateSymlinksAsync(_path/path))
|
|||
|
{
|
|||
|
if(token.IsCancellationRequested) yield break;
|
|||
|
yield return await Task.FromResult(ChopOff(item));
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public async IAsyncEnumerable<UnixPath> EnumerateFileSystemEntriesAsync(UnixPath path, [EnumeratorCancellation]CancellationToken token = default)
|
|||
|
{
|
|||
|
await foreach(var item in _fs.EnumerateFileSystemEntriesAsync(_path/path))
|
|||
|
{
|
|||
|
if(token.IsCancellationRequested) yield break;
|
|||
|
yield return await Task.FromResult(ChopOff(item));
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public void DeleteDirectory(UnixPath path, bool recursive)
|
|||
|
{
|
|||
|
_fs.DeleteDirectory(_path/path,recursive);
|
|||
|
}
|
|||
|
|
|||
|
public async Task DeleteDirectoryAsync(UnixPath path, bool recursive, CancellationToken token = default)
|
|||
|
{
|
|||
|
await _fs.DeleteDirectoryAsync(_path/path,token);
|
|||
|
}
|
|||
|
}
|
|||
|
public abstract class SyncFileSystem : IVirtualFilesystem
|
|||
|
{
|
|||
|
public virtual void Dispose()
|
|||
|
{
|
|||
|
|
|||
|
}
|
|||
|
public virtual UnixPath UpPath(UnixPath path)
|
|||
|
{
|
|||
|
return path.Parent;
|
|||
|
}
|
|||
|
public virtual UnixPath ConvertPathToUnixPath(string path)
|
|||
|
{
|
|||
|
return path;
|
|||
|
}
|
|||
|
public virtual string ConvertPathFromUnixPath(UnixPath path)
|
|||
|
{
|
|||
|
return path.ToString();
|
|||
|
}
|
|||
|
public virtual bool CanWatch(UnixPath path)
|
|||
|
{
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
public virtual bool CanHandleSymlinks(UnixPath path)
|
|||
|
{
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
public abstract void CreateDirectory(UnixPath directory);
|
|||
|
|
|||
|
public virtual async Task CreateDirectoryAsync(UnixPath directory,CancellationToken token=default)
|
|||
|
{
|
|||
|
if(token.IsCancellationRequested) return;
|
|||
|
await Task.Run(()=>CreateDirectory(directory));
|
|||
|
}
|
|||
|
|
|||
|
public virtual void CreateHardlink(UnixPath src, UnixPath dest)
|
|||
|
{
|
|||
|
this.CopyFile(src,dest);
|
|||
|
}
|
|||
|
|
|||
|
public virtual async Task CreateHardlinkAsync(UnixPath src, UnixPath dest, CancellationToken token = default)
|
|||
|
{
|
|||
|
if(token.IsCancellationRequested) return;
|
|||
|
await Task.Run(()=>CreateHardlink(src,dest));
|
|||
|
}
|
|||
|
|
|||
|
public virtual void CreateSymlink(UnixPath src, UnixPath dest)
|
|||
|
{
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
public virtual async Task CreateSymlinkAsync(UnixPath src, UnixPath dest, CancellationToken token = default)
|
|||
|
{
|
|||
|
if(token.IsCancellationRequested) return;
|
|||
|
await Task.Run(()=>CreateHardlink(src,dest));
|
|||
|
}
|
|||
|
|
|||
|
public abstract void DeleteDirectory(UnixPath path);
|
|||
|
|
|||
|
public virtual async Task DeleteDirectoryAsync(UnixPath path, CancellationToken token = default)
|
|||
|
{
|
|||
|
if(token.IsCancellationRequested) return;
|
|||
|
await Task.Run(()=>DeleteDirectory(path));
|
|||
|
}
|
|||
|
|
|||
|
public abstract void DeleteFile(UnixPath path);
|
|||
|
public virtual async Task DeleteFileAsync(UnixPath path, CancellationToken token = default)
|
|||
|
{
|
|||
|
if(token.IsCancellationRequested) return;
|
|||
|
await Task.Run(()=>DeleteFile(path));
|
|||
|
}
|
|||
|
|
|||
|
public abstract bool DirectoryExists(UnixPath path);
|
|||
|
|
|||
|
public async Task<bool> DirectoryExistsAsync(UnixPath path, CancellationToken token = default)
|
|||
|
{
|
|||
|
if(token.IsCancellationRequested) return false;
|
|||
|
return await Task.Run<bool>(()=>DirectoryExistsAsync(path));
|
|||
|
}
|
|||
|
|
|||
|
public IEnumerable<UnixPath> EnumerateDirectories(UnixPath path)
|
|||
|
{
|
|||
|
foreach(var item in EnumerateFileSystemEntries(path))
|
|||
|
{
|
|||
|
if(DirectoryExists(item) && !SymlinkExists(item))
|
|||
|
{
|
|||
|
yield return item;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public async IAsyncEnumerable<UnixPath> EnumerateDirectoriesAsync(UnixPath path, [EnumeratorCancellation]CancellationToken token = default)
|
|||
|
{
|
|||
|
await foreach(var item in EnumerateFileSystemEntriesAsync(path))
|
|||
|
{
|
|||
|
if(token.IsCancellationRequested) yield break;
|
|||
|
if(await DirectoryExistsAsync(item) && !await SymlinkExistsAsync(item))
|
|||
|
yield return await Task.FromResult(item);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public IEnumerable<UnixPath> EnumerateFiles(UnixPath path)
|
|||
|
{
|
|||
|
foreach(var item in EnumerateFileSystemEntries(path))
|
|||
|
{
|
|||
|
if(FileExists(item) && !SymlinkExists(item))
|
|||
|
{
|
|||
|
yield return item;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public async IAsyncEnumerable<UnixPath> EnumerateFilesAsync(UnixPath path, [EnumeratorCancellation] CancellationToken token = default)
|
|||
|
{
|
|||
|
await foreach(var item in EnumerateFileSystemEntriesAsync(path))
|
|||
|
{
|
|||
|
if(token.IsCancellationRequested) yield break;
|
|||
|
if(await FileExistsAsync(item) && !await SymlinkExistsAsync(item))
|
|||
|
yield return await Task.FromResult(item);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public abstract bool FileExists(UnixPath path);
|
|||
|
|
|||
|
public virtual async Task<bool> FileExistsAsync(UnixPath path, CancellationToken token = default)
|
|||
|
{
|
|||
|
if(token.IsCancellationRequested) return false;
|
|||
|
return await Task.Run<bool>(()=>FileExists(path));
|
|||
|
}
|
|||
|
|
|||
|
public virtual FileAttributes GetAttributes(UnixPath path)
|
|||
|
{
|
|||
|
return FileAttributes.Normal;
|
|||
|
}
|
|||
|
|
|||
|
public virtual async Task<FileAttributes> GetAttributesAsync(UnixPath path, CancellationToken token = default)
|
|||
|
{
|
|||
|
if(token.IsCancellationRequested) return FileAttributes.Normal;
|
|||
|
return await Task.Run<FileAttributes>(()=>GetAttributes(path));
|
|||
|
}
|
|||
|
|
|||
|
public abstract DateTime GetCreationTime(UnixPath path);
|
|||
|
|
|||
|
public virtual async Task<DateTime> GetCreationTimeAsync(UnixPath path, CancellationToken token = default)
|
|||
|
{
|
|||
|
if(token.IsCancellationRequested) return DateTime.Now;
|
|||
|
return await Task.Run<DateTime>(()=>GetCreationTime(path));
|
|||
|
}
|
|||
|
|
|||
|
public abstract DateTime GetLastAccessTime(UnixPath path);
|
|||
|
|
|||
|
public virtual async Task<DateTime> GetLastAccessTimeAsync(UnixPath path, CancellationToken token = default)
|
|||
|
{
|
|||
|
if(token.IsCancellationRequested) return DateTime.Now;
|
|||
|
return await Task.Run<DateTime>(()=>GetLastAccessTime(path));
|
|||
|
}
|
|||
|
|
|||
|
public abstract DateTime GetLastWriteTime(UnixPath path);
|
|||
|
|
|||
|
public virtual async Task<DateTime> GetLastWriteTimeAsync(UnixPath path, CancellationToken token = default)
|
|||
|
{
|
|||
|
if(token.IsCancellationRequested) return DateTime.Now;
|
|||
|
return await Task.Run<DateTime>(()=>GetLastWriteTime(path));
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
public abstract void MoveDirectory(UnixPath src, UnixPath dest);
|
|||
|
|
|||
|
public virtual async Task MoveDirectoryAsync(UnixPath src, UnixPath dest)
|
|||
|
{
|
|||
|
await Task.Run(()=>MoveDirectory(src,dest));
|
|||
|
}
|
|||
|
|
|||
|
public abstract void MoveFile(UnixPath src, UnixPath dest);
|
|||
|
|
|||
|
public virtual async Task MoveFileAsync(UnixPath src, UnixPath dest)
|
|||
|
{
|
|||
|
await Task.Run(()=>MoveFile(src,dest));
|
|||
|
}
|
|||
|
|
|||
|
public abstract Stream Open(UnixPath path, FileMode mode, FileAccess access, FileShare share);
|
|||
|
|
|||
|
public virtual async Task<Stream> OpenAsync(UnixPath path, FileMode mode, FileAccess access, FileShare share, CancellationToken token = default)
|
|||
|
{
|
|||
|
if(token.IsCancellationRequested) return Stream.Null;
|
|||
|
return await Task.Run<Stream>(()=>Open(path,mode,access,share));
|
|||
|
}
|
|||
|
|
|||
|
public virtual UnixPath ReadLink(UnixPath file)
|
|||
|
{
|
|||
|
return file;
|
|||
|
}
|
|||
|
|
|||
|
public virtual async Task<UnixPath> ReadLinkAsync(UnixPath file, CancellationToken token = default)
|
|||
|
{
|
|||
|
if(token.IsCancellationRequested) return file;
|
|||
|
return await Task.Run<UnixPath>(()=>ReadLink(file));
|
|||
|
}
|
|||
|
|
|||
|
public virtual async Task<bool> SameFileSystemAsync(UnixPath src, UnixPath dest, CancellationToken token = default)
|
|||
|
{
|
|||
|
if(token.IsCancellationRequested) return false;
|
|||
|
return await Task.Run<bool>(()=>SameFileSystem(src,dest));
|
|||
|
}
|
|||
|
|
|||
|
public virtual bool SameFileSystem(UnixPath src, UnixPath dest)
|
|||
|
{
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
public virtual void SetAttributes(UnixPath path, FileAttributes attributes)
|
|||
|
{
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
public virtual async Task SetAttributesAsync(UnixPath path, FileAttributes attributes, CancellationToken token = default)
|
|||
|
{
|
|||
|
if(token.IsCancellationRequested) return;
|
|||
|
await Task.Run(()=>SetAttributes(path,attributes));
|
|||
|
}
|
|||
|
|
|||
|
public abstract void SetCreationTime(UnixPath path, DateTime time);
|
|||
|
|
|||
|
public virtual async Task SetCreationTimeAsync(UnixPath path, DateTime time, CancellationToken token = default)
|
|||
|
{
|
|||
|
if(token.IsCancellationRequested) return;
|
|||
|
await Task.Run(()=>SetCreationTime(path,time));
|
|||
|
}
|
|||
|
|
|||
|
public abstract void SetLastAccessTime(UnixPath path, DateTime time);
|
|||
|
|
|||
|
public virtual async Task SetLastAccessTimeAsync(UnixPath path, DateTime time, CancellationToken token = default)
|
|||
|
{
|
|||
|
if(token.IsCancellationRequested) return;
|
|||
|
await Task.Run(()=>SetLastAccessTime(path,time));
|
|||
|
}
|
|||
|
|
|||
|
public abstract void SetLastWriteTime(UnixPath path, DateTime time);
|
|||
|
|
|||
|
public virtual async Task SetLastWriteTimeAsync(UnixPath path, DateTime time, CancellationToken token = default)
|
|||
|
{
|
|||
|
if(token.IsCancellationRequested) return;
|
|||
|
await Task.Run(()=>SetLastWriteTimeAsync(path,time));
|
|||
|
}
|
|||
|
|
|||
|
public virtual async Task<bool> SymlinkExistsAsync(UnixPath file, CancellationToken token = default)
|
|||
|
{
|
|||
|
if(token.IsCancellationRequested) return false;
|
|||
|
return await Task.Run<bool>(()=>SymlinkExists(file));
|
|||
|
}
|
|||
|
|
|||
|
public virtual bool SymlinkExists(UnixPath file)
|
|||
|
{
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
public virtual IVirtualWatcher WatchDirectory(UnixPath dir)
|
|||
|
{
|
|||
|
return null;
|
|||
|
}
|
|||
|
|
|||
|
public IVirtualFilesystem GetSubdirFilesystem(UnixPath path)
|
|||
|
{
|
|||
|
return new SubdirFilesystem(this,path);
|
|||
|
}
|
|||
|
public SymlinkPointer OpenSymlink(UnixPath path)
|
|||
|
{
|
|||
|
return new SymlinkPointer(this,path);
|
|||
|
}
|
|||
|
public FilePointer OpenFile(UnixPath path)
|
|||
|
{
|
|||
|
return new FilePointer(this,path);
|
|||
|
}
|
|||
|
public DirectoryPointer OpenDirectory(UnixPath path)
|
|||
|
{
|
|||
|
return new DirectoryPointer(this,path);
|
|||
|
}
|
|||
|
public DirectoryPointer OpenDirectory()
|
|||
|
{
|
|||
|
return new DirectoryPointer(this);
|
|||
|
}
|
|||
|
|
|||
|
public EntryPointer OpenEntry(UnixPath path)
|
|||
|
{
|
|||
|
if(FileExists(path))
|
|||
|
{
|
|||
|
return new FilePointer(this,path);
|
|||
|
}else if(SymlinkExists(path))
|
|||
|
{
|
|||
|
return new SymlinkPointer(this,path);
|
|||
|
}
|
|||
|
else if(DirectoryExists(path)){
|
|||
|
return new DirectoryPointer(this,path);
|
|||
|
}
|
|||
|
return new NonExistantPointer(this,path);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
public abstract IEnumerable<UnixPath> EnumerateFileSystemEntries(UnixPath path);
|
|||
|
|
|||
|
public IEnumerable<UnixPath> EnumerateSymlinks(UnixPath path)
|
|||
|
{
|
|||
|
foreach(var item in EnumerateFileSystemEntries(path))
|
|||
|
{
|
|||
|
if(SymlinkExists(item))
|
|||
|
{
|
|||
|
yield return item;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public async IAsyncEnumerable<UnixPath> EnumerateSymlinksAsync(UnixPath path, [EnumeratorCancellation]CancellationToken token = default)
|
|||
|
{
|
|||
|
await foreach(var item in EnumerateFileSystemEntriesAsync(path))
|
|||
|
{
|
|||
|
if(token.IsCancellationRequested) yield break;
|
|||
|
if(await SymlinkExistsAsync(item))
|
|||
|
yield return await Task.FromResult(item);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public virtual async IAsyncEnumerable<UnixPath> EnumerateFileSystemEntriesAsync(UnixPath path, [EnumeratorCancellation]CancellationToken token = default)
|
|||
|
{
|
|||
|
foreach(var item in EnumerateFileSystemEntries(path))
|
|||
|
{
|
|||
|
if(token.IsCancellationRequested) yield break;
|
|||
|
yield return await Task.FromResult(item);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public virtual void DeleteDirectory(UnixPath path, bool recursive)
|
|||
|
{
|
|||
|
if(recursive)
|
|||
|
{
|
|||
|
foreach(var item in EnumerateFileSystemEntries(path))
|
|||
|
{
|
|||
|
if(DirectoryExists(item))
|
|||
|
{
|
|||
|
DeleteDirectory(item,true);
|
|||
|
}else{
|
|||
|
DeleteFile(item);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
DeleteDirectory(path);
|
|||
|
}
|
|||
|
|
|||
|
public virtual async Task DeleteDirectoryAsync(UnixPath path, bool recursive, CancellationToken token = default)
|
|||
|
{
|
|||
|
if(recursive){
|
|||
|
await foreach(var item in EnumerateFileSystemEntriesAsync(path))
|
|||
|
{
|
|||
|
if(token.IsCancellationRequested) return;
|
|||
|
if(await DirectoryExistsAsync(item))
|
|||
|
{
|
|||
|
await DeleteDirectoryAsync(item,true,token);
|
|||
|
}else{
|
|||
|
await DeleteFileAsync(item,token);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
await DeleteDirectoryAsync(path,token);
|
|||
|
}
|
|||
|
}
|
|||
|
public abstract class AsyncFileSystem : IVirtualFilesystem
|
|||
|
{
|
|||
|
public SymlinkPointer OpenSymlink(UnixPath path)
|
|||
|
{
|
|||
|
return new SymlinkPointer(this,path);
|
|||
|
}
|
|||
|
public FilePointer OpenFile(UnixPath path)
|
|||
|
{
|
|||
|
return new FilePointer(this,path);
|
|||
|
}
|
|||
|
public EntryPointer OpenEntry(UnixPath path)
|
|||
|
{
|
|||
|
if(FileExists(path))
|
|||
|
{
|
|||
|
return new FilePointer(this,path);
|
|||
|
}else if(SymlinkExists(path))
|
|||
|
{
|
|||
|
return new SymlinkPointer(this,path);
|
|||
|
}
|
|||
|
else if(DirectoryExists(path)){
|
|||
|
return new DirectoryPointer(this,path);
|
|||
|
}
|
|||
|
return new NonExistantPointer(this,path);
|
|||
|
}
|
|||
|
public virtual void DeleteDirectory(UnixPath path, bool recursive)
|
|||
|
{
|
|||
|
if(recursive)
|
|||
|
{
|
|||
|
foreach(var item in EnumerateFileSystemEntries(path))
|
|||
|
{
|
|||
|
if(DirectoryExists(item))
|
|||
|
{
|
|||
|
DeleteDirectory(item,true);
|
|||
|
}else{
|
|||
|
DeleteFile(item);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
DeleteDirectory(path);
|
|||
|
}
|
|||
|
|
|||
|
public virtual async Task DeleteDirectoryAsync(UnixPath path, bool recursive, CancellationToken token = default)
|
|||
|
{
|
|||
|
if(recursive){
|
|||
|
await foreach(var item in EnumerateFileSystemEntriesAsync(path))
|
|||
|
{
|
|||
|
if(token.IsCancellationRequested) return;
|
|||
|
if(await DirectoryExistsAsync(item))
|
|||
|
{
|
|||
|
await DeleteDirectoryAsync(item,true,token);
|
|||
|
}else{
|
|||
|
await DeleteFileAsync(item,token);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
await DeleteDirectoryAsync(path,token);
|
|||
|
}
|
|||
|
|
|||
|
public IVirtualFilesystem GetSubdirFilesystem(UnixPath path)
|
|||
|
{
|
|||
|
return new SubdirFilesystem(this,path);
|
|||
|
}
|
|||
|
|
|||
|
public DirectoryPointer OpenDirectory()
|
|||
|
{
|
|||
|
return new DirectoryPointer(this);
|
|||
|
}
|
|||
|
|
|||
|
public DirectoryPointer OpenDirectory(UnixPath path)
|
|||
|
{
|
|||
|
return new DirectoryPointer(this,path);
|
|||
|
}
|
|||
|
public virtual void Dispose()
|
|||
|
{
|
|||
|
|
|||
|
}
|
|||
|
public virtual UnixPath UpPath(UnixPath path)
|
|||
|
{
|
|||
|
return path.Parent;
|
|||
|
}
|
|||
|
public virtual UnixPath ConvertPathToUnixPath(string path)
|
|||
|
{
|
|||
|
return path;
|
|||
|
}
|
|||
|
public virtual string ConvertPathFromUnixPath(UnixPath path)
|
|||
|
{
|
|||
|
return path.ToString();
|
|||
|
}
|
|||
|
public virtual bool CanWatch(UnixPath path)
|
|||
|
{
|
|||
|
return false;
|
|||
|
}
|
|||
|
public virtual void CreateDirectory(UnixPath directory)
|
|||
|
{
|
|||
|
Task.Run(async()=>await CreateDirectoryAsync(directory)).Wait();
|
|||
|
}
|
|||
|
|
|||
|
public abstract Task CreateDirectoryAsync(UnixPath directory, CancellationToken token = default);
|
|||
|
|
|||
|
public virtual void CreateHardlink(UnixPath src, UnixPath dest)
|
|||
|
{
|
|||
|
Task.Run(async()=>await CreateHardlinkAsync(src,dest)).Wait();
|
|||
|
}
|
|||
|
|
|||
|
public virtual async Task CreateHardlinkAsync(UnixPath src, UnixPath dest, CancellationToken token = default)
|
|||
|
{
|
|||
|
//await this.CopyFile()
|
|||
|
await this.CopyFileAsync(src,dest,token);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
public virtual void CreateSymlink(UnixPath src, UnixPath dest)
|
|||
|
{
|
|||
|
Task.Run(async()=>await CreateSymlinkAsync(src,dest)).Wait();
|
|||
|
}
|
|||
|
|
|||
|
public virtual async Task CreateSymlinkAsync(UnixPath src, UnixPath dest, CancellationToken token = default)
|
|||
|
{
|
|||
|
await Task.FromResult(0);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
public virtual bool CanHandleSymlinks(UnixPath path)
|
|||
|
{
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
public virtual void DeleteDirectory(UnixPath path)
|
|||
|
{
|
|||
|
Task.Run(async()=>await DeleteDirectoryAsync(path)).Wait();
|
|||
|
}
|
|||
|
|
|||
|
public abstract Task DeleteDirectoryAsync(UnixPath path, CancellationToken token = default);
|
|||
|
|
|||
|
public virtual void DeleteFile(UnixPath path)
|
|||
|
{
|
|||
|
Task.Run(async()=>await DeleteFileAsync(path)).Wait();
|
|||
|
}
|
|||
|
|
|||
|
public abstract Task DeleteFileAsync(UnixPath path, CancellationToken token = default);
|
|||
|
|
|||
|
public virtual bool DirectoryExists(UnixPath path)
|
|||
|
{
|
|||
|
bool res=false;
|
|||
|
Task.Run(async()=>res=await DirectoryExistsAsync(path)).Wait();
|
|||
|
return res;
|
|||
|
}
|
|||
|
|
|||
|
public abstract Task<bool> DirectoryExistsAsync(UnixPath path, CancellationToken token = default);
|
|||
|
|
|||
|
public virtual IEnumerable<UnixPath> EnumerateDirectories(UnixPath path)
|
|||
|
{
|
|||
|
var enumerator = EnumerateDirectoriesAsync(path).GetAsyncEnumerator();
|
|||
|
while(Task.Run<bool>(async()=>await enumerator.MoveNextAsync()).GetAwaiter().GetResult())
|
|||
|
{
|
|||
|
yield return enumerator.Current;
|
|||
|
}
|
|||
|
Task.Run(enumerator.DisposeAsync).Wait();
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
public virtual IEnumerable<UnixPath> EnumerateFiles(UnixPath path)
|
|||
|
{
|
|||
|
var enumerator = EnumerateFilesAsync(path).GetAsyncEnumerator();
|
|||
|
while(Task.Run<bool>(async()=>await enumerator.MoveNextAsync()).GetAwaiter().GetResult())
|
|||
|
{
|
|||
|
yield return enumerator.Current;
|
|||
|
}
|
|||
|
Task.Run(enumerator.DisposeAsync).Wait();
|
|||
|
}
|
|||
|
|
|||
|
public virtual async IAsyncEnumerable<UnixPath> EnumerateFilesAsync(UnixPath path, [EnumeratorCancellation] CancellationToken token = default)
|
|||
|
{
|
|||
|
await foreach(var item in EnumerateFileSystemEntriesAsync(path,token))
|
|||
|
{
|
|||
|
if(token.IsCancellationRequested) yield break;
|
|||
|
if(await FileExistsAsync(item,token) && !await SymlinkExistsAsync(item,token))
|
|||
|
{
|
|||
|
yield return await Task.FromResult(item);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
public virtual async IAsyncEnumerable<UnixPath> EnumerateDirectoriesAsync(UnixPath path, [EnumeratorCancellation] CancellationToken token = default)
|
|||
|
{
|
|||
|
await foreach(var item in EnumerateFileSystemEntriesAsync(path,token))
|
|||
|
{
|
|||
|
if(token.IsCancellationRequested) yield break;
|
|||
|
if(await DirectoryExistsAsync(item,token) && !await SymlinkExistsAsync(item,token))
|
|||
|
{
|
|||
|
yield return await Task.FromResult(item);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
public virtual async IAsyncEnumerable<UnixPath> EnumerateSymlinksAsync(UnixPath path, [EnumeratorCancellation] CancellationToken token = default)
|
|||
|
{
|
|||
|
await foreach(var item in EnumerateFileSystemEntriesAsync(path,token))
|
|||
|
{
|
|||
|
if(token.IsCancellationRequested) yield break;
|
|||
|
if(await SymlinkExistsAsync(item,token))
|
|||
|
{
|
|||
|
yield return await Task.FromResult(item);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
public virtual bool FileExists(UnixPath path)
|
|||
|
{
|
|||
|
|
|||
|
return Task.Run<bool>(async()=>await FileExistsAsync(path)).GetAwaiter().GetResult();
|
|||
|
}
|
|||
|
|
|||
|
public abstract Task<bool> FileExistsAsync(UnixPath path, CancellationToken token = default);
|
|||
|
|
|||
|
public virtual FileAttributes GetAttributes(UnixPath path)
|
|||
|
{
|
|||
|
return Task.Run<FileAttributes>(async()=>await GetAttributesAsync(path)).GetAwaiter().GetResult();
|
|||
|
}
|
|||
|
|
|||
|
public virtual Task<FileAttributes> GetAttributesAsync(UnixPath path, CancellationToken token = default)
|
|||
|
{
|
|||
|
return Task.FromResult(FileAttributes.Normal);
|
|||
|
}
|
|||
|
|
|||
|
public virtual DateTime GetCreationTime(UnixPath path)
|
|||
|
{
|
|||
|
return Task.Run<DateTime>(async()=>await GetCreationTimeAsync(path)).GetAwaiter().GetResult();
|
|||
|
}
|
|||
|
|
|||
|
public abstract Task<DateTime> GetCreationTimeAsync(UnixPath path, CancellationToken token = default);
|
|||
|
|
|||
|
public virtual DateTime GetLastAccessTime(UnixPath path)
|
|||
|
{
|
|||
|
return Task.Run<DateTime>(async()=>await GetLastAccessTimeAsync(path)).GetAwaiter().GetResult();
|
|||
|
}
|
|||
|
|
|||
|
public abstract Task<DateTime> GetLastAccessTimeAsync(UnixPath path, CancellationToken token = default);
|
|||
|
|
|||
|
public virtual DateTime GetLastWriteTime(UnixPath path)
|
|||
|
{
|
|||
|
return Task.Run<DateTime>(async()=>await GetLastWriteTimeAsync(path)).GetAwaiter().GetResult();
|
|||
|
}
|
|||
|
|
|||
|
public abstract Task<DateTime> GetLastWriteTimeAsync(UnixPath path, CancellationToken token = default);
|
|||
|
|
|||
|
|
|||
|
public virtual void MoveDirectory(UnixPath src, UnixPath dest)
|
|||
|
{
|
|||
|
Task.Run(async()=>await MoveDirectoryAsync(src,dest)).Wait();
|
|||
|
}
|
|||
|
|
|||
|
public abstract Task MoveDirectoryAsync(UnixPath src, UnixPath dest);
|
|||
|
|
|||
|
public virtual void MoveFile(UnixPath src, UnixPath dest)
|
|||
|
{
|
|||
|
Task.Run(async()=>await MoveFileAsync(src,dest)).Wait();
|
|||
|
}
|
|||
|
|
|||
|
public abstract Task MoveFileAsync(UnixPath src, UnixPath dest);
|
|||
|
|
|||
|
public Stream Open(UnixPath path, FileMode mode, FileAccess access, FileShare share)
|
|||
|
{
|
|||
|
return Task.Run<Stream>(async()=>await OpenAsync(path,mode,access,share)).GetAwaiter().GetResult();
|
|||
|
}
|
|||
|
|
|||
|
public abstract Task<Stream> OpenAsync(UnixPath path, FileMode mode, FileAccess access, FileShare share, CancellationToken token = default);
|
|||
|
|
|||
|
public virtual UnixPath ReadLink(UnixPath file)
|
|||
|
{
|
|||
|
return Task.Run<UnixPath>(async()=>await ReadLinkAsync(file)).GetAwaiter().GetResult();
|
|||
|
}
|
|||
|
|
|||
|
public virtual async Task<UnixPath> ReadLinkAsync(UnixPath file, CancellationToken token = default)
|
|||
|
{
|
|||
|
return await Task.FromResult(file);
|
|||
|
}
|
|||
|
|
|||
|
public virtual async Task<bool> SameFileSystemAsync(UnixPath src, UnixPath dest, CancellationToken token = default)
|
|||
|
{
|
|||
|
return await Task.FromResult(false);
|
|||
|
}
|
|||
|
|
|||
|
public virtual bool SameFileSystem(UnixPath src, UnixPath dest)
|
|||
|
{
|
|||
|
return Task.Run<bool>(async()=>await SameFileSystemAsync(src,dest)).GetAwaiter().GetResult();
|
|||
|
}
|
|||
|
|
|||
|
public virtual void SetAttributes(UnixPath path, FileAttributes attributes)
|
|||
|
{
|
|||
|
Task.Run(async()=>await SetAttributesAsync(path,attributes)).Wait();
|
|||
|
}
|
|||
|
|
|||
|
public virtual async Task SetAttributesAsync(UnixPath path, FileAttributes attributes, CancellationToken token = default)
|
|||
|
{
|
|||
|
await Task.FromResult(0);
|
|||
|
}
|
|||
|
|
|||
|
public virtual void SetCreationTime(UnixPath path, DateTime time)
|
|||
|
{
|
|||
|
Task.Run(async()=>await SetCreationTimeAsync(path,time)).Wait();
|
|||
|
}
|
|||
|
|
|||
|
public abstract Task SetCreationTimeAsync(UnixPath path, DateTime time, CancellationToken token = default);
|
|||
|
|
|||
|
public virtual void SetLastAccessTime(UnixPath path, DateTime time)
|
|||
|
{
|
|||
|
Task.Run(async()=>await SetLastAccessTimeAsync(path,time)).Wait();
|
|||
|
}
|
|||
|
|
|||
|
public abstract Task SetLastAccessTimeAsync(UnixPath path, DateTime time, CancellationToken token = default);
|
|||
|
|
|||
|
public virtual void SetLastWriteTime(UnixPath path, DateTime time)
|
|||
|
{
|
|||
|
Task.Run(async()=>await SetLastWriteTimeAsync(path,time)).Wait();
|
|||
|
}
|
|||
|
|
|||
|
public abstract Task SetLastWriteTimeAsync(UnixPath path, DateTime time, CancellationToken token = default);
|
|||
|
|
|||
|
public virtual async Task<bool> SymlinkExistsAsync(UnixPath file, CancellationToken token = default)
|
|||
|
{
|
|||
|
return await Task.FromResult(false);
|
|||
|
}
|
|||
|
|
|||
|
public virtual bool SymlinkExists(UnixPath file)
|
|||
|
{
|
|||
|
return Task.Run<bool>(async()=>await SymlinkExistsAsync(file)).GetAwaiter().GetResult();
|
|||
|
}
|
|||
|
|
|||
|
public virtual IVirtualWatcher WatchDirectory(UnixPath dir)
|
|||
|
{
|
|||
|
return null;
|
|||
|
}
|
|||
|
|
|||
|
public IEnumerable<UnixPath> EnumerateFileSystemEntries(UnixPath path)
|
|||
|
{
|
|||
|
var enumerator = EnumerateFileSystemEntriesAsync(path).GetAsyncEnumerator();
|
|||
|
while(Task.Run<bool>(async()=>await enumerator.MoveNextAsync()).GetAwaiter().GetResult())
|
|||
|
{
|
|||
|
yield return enumerator.Current;
|
|||
|
}
|
|||
|
Task.Run(enumerator.DisposeAsync).Wait();
|
|||
|
}
|
|||
|
|
|||
|
public IEnumerable<UnixPath> EnumerateSymlinks(UnixPath path)
|
|||
|
{
|
|||
|
var enumerator = EnumerateSymlinksAsync(path).GetAsyncEnumerator();
|
|||
|
while(Task.Run<bool>(async()=>await enumerator.MoveNextAsync()).GetAwaiter().GetResult())
|
|||
|
{
|
|||
|
yield return enumerator.Current;
|
|||
|
}
|
|||
|
Task.Run(enumerator.DisposeAsync).Wait();
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
public abstract IAsyncEnumerable<UnixPath> EnumerateFileSystemEntriesAsync(UnixPath path, CancellationToken token = default);
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
public sealed class MultiLayerFS : AsyncFileSystem
|
|||
|
{
|
|||
|
private delegate Task<Stream> OpenAsyncDel(UnixPath path, FileMode mode, FileAccess access, FileShare share, CancellationToken token = default);
|
|||
|
private class FileSystemWithCustomOpen : IVirtualFilesystem
|
|||
|
{
|
|||
|
|
|||
|
public FileSystemWithCustomOpen(IVirtualFilesystem fs,OpenAsyncDel del)
|
|||
|
{
|
|||
|
this.del = del;
|
|||
|
this.fs = fs;
|
|||
|
}
|
|||
|
OpenAsyncDel del;
|
|||
|
public IVirtualFilesystem fs;
|
|||
|
|
|||
|
public bool CanHandleSymlinks(UnixPath path)
|
|||
|
{
|
|||
|
return fs.CanHandleSymlinks(path);
|
|||
|
}
|
|||
|
|
|||
|
public bool CanWatch(UnixPath path)
|
|||
|
{
|
|||
|
return fs.CanWatch(path);
|
|||
|
}
|
|||
|
|
|||
|
public string ConvertPathFromUnixPath(UnixPath path)
|
|||
|
{
|
|||
|
return fs.ConvertPathFromUnixPath(path);
|
|||
|
}
|
|||
|
|
|||
|
public UnixPath ConvertPathToUnixPath(string path)
|
|||
|
{
|
|||
|
return fs.ConvertPathToUnixPath(path);
|
|||
|
}
|
|||
|
|
|||
|
public void CreateDirectory(UnixPath directory)
|
|||
|
{
|
|||
|
fs.CreateDirectory(directory);
|
|||
|
}
|
|||
|
|
|||
|
public async Task CreateDirectoryAsync(UnixPath directory, CancellationToken token = default)
|
|||
|
{
|
|||
|
await fs.CreateDirectoryAsync(directory,token);
|
|||
|
}
|
|||
|
|
|||
|
public void CreateHardlink(UnixPath src, UnixPath dest)
|
|||
|
{
|
|||
|
fs.CreateHardlink(src,dest);
|
|||
|
}
|
|||
|
|
|||
|
public async Task CreateHardlinkAsync(UnixPath src, UnixPath dest, CancellationToken token = default)
|
|||
|
{
|
|||
|
await fs.CreateHardlinkAsync(src,dest,token);
|
|||
|
}
|
|||
|
|
|||
|
public void CreateSymlink(UnixPath src, UnixPath dest)
|
|||
|
{
|
|||
|
fs.CreateSymlink(src,dest);
|
|||
|
}
|
|||
|
|
|||
|
public async Task CreateSymlinkAsync(UnixPath src, UnixPath dest, CancellationToken token = default)
|
|||
|
{
|
|||
|
await fs.CreateSymlinkAsync(src,dest,token);
|
|||
|
}
|
|||
|
|
|||
|
public void DeleteDirectory(UnixPath path)
|
|||
|
{
|
|||
|
fs.DeleteDirectory(path);
|
|||
|
}
|
|||
|
|
|||
|
public async Task DeleteDirectoryAsync(UnixPath path, CancellationToken token = default)
|
|||
|
{
|
|||
|
await fs.DeleteDirectoryAsync(path,token);
|
|||
|
}
|
|||
|
|
|||
|
public void DeleteFile(UnixPath path)
|
|||
|
{
|
|||
|
fs.DeleteFile(path);
|
|||
|
}
|
|||
|
|
|||
|
public async Task DeleteFileAsync(UnixPath path, CancellationToken token = default)
|
|||
|
{
|
|||
|
await fs.DeleteFileAsync(path,token);
|
|||
|
}
|
|||
|
|
|||
|
public bool DirectoryExists(UnixPath path)
|
|||
|
{
|
|||
|
return fs.DirectoryExists(path);
|
|||
|
}
|
|||
|
|
|||
|
public async Task<bool> DirectoryExistsAsync(UnixPath path, CancellationToken token = default)
|
|||
|
{
|
|||
|
return await fs.DirectoryExistsAsync(path,token);
|
|||
|
}
|
|||
|
|
|||
|
public void Dispose()
|
|||
|
{
|
|||
|
fs.Dispose();
|
|||
|
}
|
|||
|
|
|||
|
public IEnumerable<UnixPath> EnumerateDirectories(UnixPath path)
|
|||
|
{
|
|||
|
foreach(var item in fs.EnumerateDirectories(path))
|
|||
|
{
|
|||
|
yield return item;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public async IAsyncEnumerable<UnixPath> EnumerateDirectoriesAsync(UnixPath path, [EnumeratorCancellation]CancellationToken token = default)
|
|||
|
{
|
|||
|
await foreach(var item in fs.EnumerateDirectoriesAsync(path,token))
|
|||
|
{
|
|||
|
if(token.IsCancellationRequested) yield break;
|
|||
|
yield return item;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public IEnumerable<UnixPath> EnumerateFiles(UnixPath path)
|
|||
|
{
|
|||
|
foreach(var item in fs.EnumerateFiles(path))
|
|||
|
{
|
|||
|
yield return item;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public async IAsyncEnumerable<UnixPath> EnumerateFilesAsync(UnixPath path, [EnumeratorCancellation]CancellationToken token = default)
|
|||
|
{
|
|||
|
await foreach(var item in fs.EnumerateFilesAsync(path,token))
|
|||
|
{
|
|||
|
if(token.IsCancellationRequested) yield break;
|
|||
|
yield return path;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public bool FileExists(UnixPath path)
|
|||
|
{
|
|||
|
return fs.FileExists(path);
|
|||
|
}
|
|||
|
|
|||
|
public async Task<bool> FileExistsAsync(UnixPath path, CancellationToken token = default)
|
|||
|
{
|
|||
|
return await fs.FileExistsAsync(path,token);
|
|||
|
}
|
|||
|
|
|||
|
public FileAttributes GetAttributes(UnixPath path)
|
|||
|
{
|
|||
|
return fs.GetAttributes(path);
|
|||
|
}
|
|||
|
|
|||
|
public async Task<FileAttributes> GetAttributesAsync(UnixPath path, CancellationToken token = default)
|
|||
|
{
|
|||
|
return await fs.GetAttributesAsync(path,token);
|
|||
|
}
|
|||
|
|
|||
|
public DateTime GetCreationTime(UnixPath path)
|
|||
|
{
|
|||
|
return fs.GetCreationTime(path);
|
|||
|
}
|
|||
|
|
|||
|
public async Task<DateTime> GetCreationTimeAsync(UnixPath path, CancellationToken token = default)
|
|||
|
{
|
|||
|
return await fs.GetCreationTimeAsync(path,token);
|
|||
|
}
|
|||
|
|
|||
|
public DateTime GetLastAccessTime(UnixPath path)
|
|||
|
{
|
|||
|
return fs.GetLastAccessTime(path);
|
|||
|
}
|
|||
|
|
|||
|
public async Task<DateTime> GetLastAccessTimeAsync(UnixPath path, CancellationToken token = default)
|
|||
|
{
|
|||
|
return await fs.GetLastAccessTimeAsync(path,token);
|
|||
|
}
|
|||
|
|
|||
|
public DateTime GetLastWriteTime(UnixPath path)
|
|||
|
{
|
|||
|
return fs.GetLastAccessTime(path);
|
|||
|
}
|
|||
|
|
|||
|
public async Task<DateTime> GetLastWriteTimeAsync(UnixPath path, CancellationToken token = default)
|
|||
|
{
|
|||
|
return await fs.GetLastWriteTimeAsync(path,token);
|
|||
|
}
|
|||
|
|
|||
|
public void MoveDirectory(UnixPath src, UnixPath dest)
|
|||
|
{
|
|||
|
fs.MoveDirectory(src,dest);
|
|||
|
}
|
|||
|
|
|||
|
public async Task MoveDirectoryAsync(UnixPath src, UnixPath dest)
|
|||
|
{
|
|||
|
await fs.MoveDirectoryAsync(src,dest);
|
|||
|
}
|
|||
|
|
|||
|
public void MoveFile(UnixPath src, UnixPath dest)
|
|||
|
{
|
|||
|
fs.MoveFile(src,dest);
|
|||
|
}
|
|||
|
|
|||
|
public async Task MoveFileAsync(UnixPath src, UnixPath dest)
|
|||
|
{
|
|||
|
await fs.MoveFileAsync(src,dest);
|
|||
|
}
|
|||
|
|
|||
|
public Stream Open(UnixPath path, FileMode mode, FileAccess access, FileShare share)
|
|||
|
{
|
|||
|
return Task.Run<Stream>(async()=>await del(path,mode,access,share)).GetAwaiter().GetResult();
|
|||
|
}
|
|||
|
|
|||
|
public async Task<Stream> OpenAsync(UnixPath path, FileMode mode, FileAccess access, FileShare share, CancellationToken token = default)
|
|||
|
{
|
|||
|
return await del(path,mode,access,share,token);
|
|||
|
}
|
|||
|
|
|||
|
public UnixPath ReadLink(UnixPath file)
|
|||
|
{
|
|||
|
return fs.ReadLink(file);
|
|||
|
}
|
|||
|
|
|||
|
public async Task<UnixPath> ReadLinkAsync(UnixPath file, CancellationToken token = default)
|
|||
|
{
|
|||
|
return await fs.ReadLinkAsync(file,token);
|
|||
|
}
|
|||
|
|
|||
|
public bool SameFileSystem(UnixPath src, UnixPath dest)
|
|||
|
{
|
|||
|
return fs.SameFileSystem(src,dest);
|
|||
|
}
|
|||
|
|
|||
|
public async Task<bool> SameFileSystemAsync(UnixPath src, UnixPath dest, CancellationToken token = default)
|
|||
|
{
|
|||
|
return await fs.SameFileSystemAsync(src,dest,token);
|
|||
|
}
|
|||
|
|
|||
|
public void SetAttributes(UnixPath path, FileAttributes attributes)
|
|||
|
{
|
|||
|
fs.SetAttributes(path,attributes);
|
|||
|
}
|
|||
|
|
|||
|
public async Task SetAttributesAsync(UnixPath path, FileAttributes attributes, CancellationToken token = default)
|
|||
|
{
|
|||
|
await fs.SetAttributesAsync(path,attributes,token);
|
|||
|
}
|
|||
|
|
|||
|
public void SetCreationTime(UnixPath path, DateTime time)
|
|||
|
{
|
|||
|
fs.SetCreationTime(path,time);
|
|||
|
}
|
|||
|
|
|||
|
public async Task SetCreationTimeAsync(UnixPath path, DateTime time, CancellationToken token = default)
|
|||
|
{
|
|||
|
await fs.SetCreationTimeAsync(path,time,token);
|
|||
|
}
|
|||
|
|
|||
|
public void SetLastAccessTime(UnixPath path, DateTime time)
|
|||
|
{
|
|||
|
fs.SetLastAccessTime(path,time);
|
|||
|
}
|
|||
|
|
|||
|
public async Task SetLastAccessTimeAsync(UnixPath path, DateTime time, CancellationToken token = default)
|
|||
|
{
|
|||
|
await fs.SetLastAccessTimeAsync(path,time,token);
|
|||
|
}
|
|||
|
|
|||
|
public void SetLastWriteTime(UnixPath path, DateTime time)
|
|||
|
{
|
|||
|
fs.SetLastWriteTime(path,time);
|
|||
|
}
|
|||
|
|
|||
|
public async Task SetLastWriteTimeAsync(UnixPath path, DateTime time, CancellationToken token = default)
|
|||
|
{
|
|||
|
await fs.SetLastWriteTimeAsync(path,time,token);
|
|||
|
}
|
|||
|
|
|||
|
public bool SymlinkExists(UnixPath file)
|
|||
|
{
|
|||
|
return fs.SymlinkExists(file);
|
|||
|
}
|
|||
|
|
|||
|
public async Task<bool> SymlinkExistsAsync(UnixPath file, CancellationToken token = default)
|
|||
|
{
|
|||
|
return await fs.SymlinkExistsAsync(file,token);
|
|||
|
}
|
|||
|
|
|||
|
public UnixPath UpPath(UnixPath path)
|
|||
|
{
|
|||
|
return fs.UpPath(path);
|
|||
|
}
|
|||
|
|
|||
|
public IVirtualWatcher WatchDirectory(UnixPath dir)
|
|||
|
{
|
|||
|
return fs.WatchDirectory(dir);
|
|||
|
}
|
|||
|
}
|
|||
|
IVirtualFilesystem root;
|
|||
|
public MultiLayerFS(IVirtualFilesystem root)
|
|||
|
{
|
|||
|
this.root=root;
|
|||
|
}
|
|||
|
public class CloseEvtStream : Stream
|
|||
|
{
|
|||
|
Stream parent;
|
|||
|
Action cstrm;
|
|||
|
bool destroyParent;
|
|||
|
public CloseEvtStream(Stream parent,Action closed,bool destroyParent=true)
|
|||
|
{
|
|||
|
this.destroyParent=destroyParent;
|
|||
|
this.parent = parent;
|
|||
|
this.cstrm = closed;
|
|||
|
}
|
|||
|
public override bool CanRead => parent.CanRead;
|
|||
|
|
|||
|
public override bool CanSeek => parent.CanSeek;
|
|||
|
|
|||
|
public override bool CanWrite => parent.CanWrite;
|
|||
|
|
|||
|
public override long Length => parent.Length;
|
|||
|
|
|||
|
public override long Position { get => parent.Position; set => parent.Position = value; }
|
|||
|
|
|||
|
public override void Flush()
|
|||
|
{
|
|||
|
parent.Flush();
|
|||
|
}
|
|||
|
|
|||
|
public override int Read(byte[] buffer, int offset, int count)
|
|||
|
{
|
|||
|
return parent.Read(buffer,offset,count);
|
|||
|
}
|
|||
|
|
|||
|
public override long Seek(long offset, SeekOrigin origin)
|
|||
|
{
|
|||
|
return parent.Seek(offset,origin);
|
|||
|
}
|
|||
|
|
|||
|
public override void SetLength(long value)
|
|||
|
{
|
|||
|
parent.SetLength(value);
|
|||
|
}
|
|||
|
|
|||
|
public override void Write(byte[] buffer, int offset, int count)
|
|||
|
{
|
|||
|
parent.Write(buffer,offset,count);
|
|||
|
}
|
|||
|
public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
|
|||
|
{
|
|||
|
return parent.BeginRead(buffer, offset, count, callback, state);
|
|||
|
}
|
|||
|
public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
|
|||
|
{
|
|||
|
return parent.BeginWrite(buffer, offset, count, callback, state);
|
|||
|
}
|
|||
|
public override bool CanTimeout => parent.CanTimeout;
|
|||
|
public override void Close()
|
|||
|
{
|
|||
|
if(destroyParent)
|
|||
|
parent.Close();
|
|||
|
cstrm();
|
|||
|
}
|
|||
|
protected override void Dispose(bool disposing)
|
|||
|
{
|
|||
|
if(disposing && destroyParent)
|
|||
|
parent.Dispose();
|
|||
|
cstrm();
|
|||
|
}
|
|||
|
public override void EndWrite(IAsyncResult asyncResult)
|
|||
|
{
|
|||
|
parent.EndWrite(asyncResult);
|
|||
|
}
|
|||
|
|
|||
|
public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
|
|||
|
{
|
|||
|
return parent.WriteAsync(buffer, offset, count, cancellationToken);
|
|||
|
}
|
|||
|
public override Task FlushAsync(CancellationToken cancellationToken)
|
|||
|
{
|
|||
|
return parent.FlushAsync(cancellationToken);
|
|||
|
}
|
|||
|
public override void WriteByte(byte value)
|
|||
|
{
|
|||
|
parent.WriteByte(value);
|
|||
|
}
|
|||
|
public override int EndRead(IAsyncResult asyncResult)
|
|||
|
{
|
|||
|
return parent.EndRead(asyncResult);
|
|||
|
}
|
|||
|
public override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken)
|
|||
|
{
|
|||
|
return parent.CopyToAsync(destination, bufferSize, cancellationToken);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
List<IVirtualFilesystem> fs2=new List<IVirtualFilesystem>();
|
|||
|
Dictionary<IVirtualFilesystem,(List<Stream> StreamList,Action2Event Destroy)> streams = new Dictionary<IVirtualFilesystem, (List<Stream> StreamList,Action2Event Destroy)>();
|
|||
|
Dictionary<string,IVirtualFilesystem> fs=new Dictionary<string, IVirtualFilesystem>();
|
|||
|
public override UnixPath UpPath(UnixPath path)
|
|||
|
{
|
|||
|
var unixPathLs = path as UnixPathList;
|
|||
|
if(unixPathLs != null)
|
|||
|
{
|
|||
|
return unixPathLs.ParentPL;
|
|||
|
}else{
|
|||
|
return path.Parent;
|
|||
|
}
|
|||
|
}
|
|||
|
Dictionary<string,IVirtualFileSystemCreator> _creators=new Dictionary<string,IVirtualFileSystemCreator>();
|
|||
|
public void Register(string scheme,IVirtualFileSystemCreator fs)
|
|||
|
{
|
|||
|
_creators.Add(scheme,fs);
|
|||
|
}
|
|||
|
public void Register(IVirtualFileSystemCreator fs)
|
|||
|
{
|
|||
|
_creators.Add(fs.Scheme,fs);
|
|||
|
}
|
|||
|
public override void Dispose()
|
|||
|
{
|
|||
|
while(fs2.Count>0 && streams.Count > 0)
|
|||
|
{
|
|||
|
if(streams.ContainsKey(fs2[0]))
|
|||
|
{
|
|||
|
var strm = streams[fs2[0]];
|
|||
|
foreach(var strm0 in strm.StreamList)
|
|||
|
{
|
|||
|
strm0.Dispose();
|
|||
|
}
|
|||
|
strm.StreamList.Clear();
|
|||
|
CloseFileSystemIfNoStreams(fs2[0]);
|
|||
|
streams.Remove(fs2[0]);
|
|||
|
fs2.Remove(fs2[0]);
|
|||
|
}
|
|||
|
}
|
|||
|
streams.Clear();
|
|||
|
fs.Clear();
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
public override async Task CreateDirectoryAsync(UnixPath directory, CancellationToken token = default)
|
|||
|
{
|
|||
|
var fs=await OpenTillAsync(directory,token);
|
|||
|
await fs.CreateDirectoryAsync(Last(directory));
|
|||
|
CloseFileSystemIfNoStreams(fs);
|
|||
|
}
|
|||
|
private async Task<IVirtualFilesystem> OpenTillAsync(UnixPath path,CancellationToken token=default)
|
|||
|
{
|
|||
|
UnixPathList pathLs = path as UnixPathList;
|
|||
|
if(pathLs != null && pathLs.Count > 1)
|
|||
|
{
|
|||
|
return await OpenTillAsync(pathLs,token);
|
|||
|
}
|
|||
|
return root;
|
|||
|
}
|
|||
|
|
|||
|
private async Task<IVirtualFilesystem> OpenTillAsync(UnixPathList path,CancellationToken token=default)
|
|||
|
{
|
|||
|
|
|||
|
IVirtualFilesystem fs=root;
|
|||
|
List<UnixPath> paths=new List<UnixPath>();
|
|||
|
for(int i = 0;i<path.Count-1;i++)
|
|||
|
{
|
|||
|
paths.Add(path[i]);
|
|||
|
var curP = new UnixPathList(paths);
|
|||
|
|
|||
|
fs=await OpenFileSystemAsync(fs,curP,token);
|
|||
|
if(token.IsCancellationRequested) {this.CloseFileSystemIfNoStreams(fs); return null;}
|
|||
|
}
|
|||
|
return fs;
|
|||
|
}
|
|||
|
Mutex mtx=new Mutex();
|
|||
|
|
|||
|
private async Task<Stream> OpenStreamOnBehalf(IVirtualFilesystem fs,UnixPath p,FileMode mode,FileAccess access,FileShare share,CancellationToken token=default(CancellationToken))
|
|||
|
{
|
|||
|
|
|||
|
Stream strm = await fs.OpenAsync(p,mode,access,share);
|
|||
|
CloseEvtStream strmI=new CloseEvtStream(strm,()=>{
|
|||
|
mtx.WaitOne();
|
|||
|
if(streams.ContainsKey(fs))
|
|||
|
{
|
|||
|
var strmItem = streams[fs];
|
|||
|
if(strmItem.StreamList.Contains(strm))
|
|||
|
{
|
|||
|
strmItem.StreamList.Remove(strm);
|
|||
|
|
|||
|
}
|
|||
|
if(strmItem.StreamList.Count == 0)
|
|||
|
{
|
|||
|
strmItem.Destroy.Invoke();
|
|||
|
streams.Remove(fs);
|
|||
|
string key = "";
|
|||
|
foreach(var i in this.fs)
|
|||
|
{
|
|||
|
if(i.Value == fs)
|
|||
|
{
|
|||
|
key = i.Key;
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
if(this.fs.ContainsKey(key))
|
|||
|
{
|
|||
|
this.fs.Remove(key);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
mtx.ReleaseMutex();
|
|||
|
});
|
|||
|
mtx.WaitOne();
|
|||
|
if(streams.ContainsKey(fs))
|
|||
|
{
|
|||
|
var strmItem = streams[fs];
|
|||
|
if(!strmItem.StreamList.Contains(strm))
|
|||
|
{
|
|||
|
strmItem.StreamList.Add(strm);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
mtx.ReleaseMutex();
|
|||
|
|
|||
|
return strmI;
|
|||
|
}
|
|||
|
public UnixPath ExceptLast(UnixPath p)
|
|||
|
{
|
|||
|
UnixPathList pl = p as UnixPathList;
|
|||
|
if(pl != null && pl.Count > 0)
|
|||
|
{
|
|||
|
List<UnixPath> paths=new List<UnixPath>();
|
|||
|
for(int i = 0;i<pl.Count-1;i++)
|
|||
|
{
|
|||
|
paths.Add(pl[i]);
|
|||
|
}
|
|||
|
return new UnixPathList(paths);
|
|||
|
}
|
|||
|
|
|||
|
return p;
|
|||
|
}
|
|||
|
private UnixPath Last(UnixPath p)
|
|||
|
{
|
|||
|
UnixPathList pl=p as UnixPathList;
|
|||
|
if(pl!=null && pl.Count >0)
|
|||
|
{
|
|||
|
|
|||
|
return pl[pl.Count-1];
|
|||
|
}
|
|||
|
return p;
|
|||
|
}
|
|||
|
private async Task<IVirtualFilesystem> OpenFileSystemAsync(IVirtualFilesystem parent,UnixPath p,CancellationToken token=default)
|
|||
|
{
|
|||
|
if(string.IsNullOrWhiteSpace(p.Scheme)) return parent;
|
|||
|
mtx.WaitOne();
|
|||
|
var last=Last(p);
|
|||
|
if(!_creators.ContainsKey(last.Scheme)){ mtx.ReleaseMutex();return null;}
|
|||
|
if(parent == root)
|
|||
|
{
|
|||
|
if(fs.ContainsKey(p.AsText))
|
|||
|
{
|
|||
|
var res0= fs[p.AsText];
|
|||
|
mtx.ReleaseMutex();
|
|||
|
return res0;
|
|||
|
}
|
|||
|
var res=_creators[p.Scheme];
|
|||
|
if(res.IsDirectoryOrganizer)
|
|||
|
{
|
|||
|
var c = res.Open(root,last);
|
|||
|
fs.Add(p.AsText,c);
|
|||
|
|
|||
|
return c;
|
|||
|
}else{
|
|||
|
var strm = await OpenAsync(last,FileMode.Open,FileAccess.ReadWrite,FileShare.None,token);
|
|||
|
var c = res.Open(strm);
|
|||
|
Action a =()=>{
|
|||
|
c.Dispose();
|
|||
|
strm.Dispose();
|
|||
|
|
|||
|
};
|
|||
|
streams.Add(c,(new List<Stream>(),a));
|
|||
|
fs.Add(p.AsText,c);
|
|||
|
return c;
|
|||
|
}
|
|||
|
|
|||
|
}else{mtx.ReleaseMutex();
|
|||
|
if(fs.ContainsKey(p.AsText))
|
|||
|
{
|
|||
|
var res0= fs[p.AsText];
|
|||
|
mtx.ReleaseMutex();
|
|||
|
return res0;
|
|||
|
}
|
|||
|
var res=_creators[p.Scheme];
|
|||
|
if(res.IsDirectoryOrganizer)
|
|||
|
{
|
|||
|
var parent2 = new FileSystemWithCustomOpen(parent,async(path,mode,access,share,token)=>{
|
|||
|
return await OpenStreamOnBehalf(parent,path,mode,access,share,token);
|
|||
|
});
|
|||
|
var c = res.Open(parent2,last);
|
|||
|
streams[parent].Destroy.Add(c.Dispose);
|
|||
|
fs2.Insert(0,c);
|
|||
|
fs.Add(p.AsText,c);
|
|||
|
return c;
|
|||
|
}else{
|
|||
|
var res0=await OpenStreamOnBehalf(parent,last,FileMode.Open,FileAccess.ReadWrite,FileShare.None,token);
|
|||
|
var c = res.Open(res0);
|
|||
|
Action a =()=>{
|
|||
|
if(fs2.Contains(c))
|
|||
|
fs2.Remove(c);
|
|||
|
c.Dispose();
|
|||
|
res0.Dispose();
|
|||
|
|
|||
|
};
|
|||
|
|
|||
|
streams.Add(c,(new List<Stream>(),a));
|
|||
|
fs.Add(p.AsText,c);
|
|||
|
fs2.Insert(0,c);
|
|||
|
mtx.ReleaseMutex();
|
|||
|
return c;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
private void CloseFileSystemIfNoStreams(IVirtualFilesystem fs)
|
|||
|
{
|
|||
|
if(fs == root) return;
|
|||
|
mtx.WaitOne();
|
|||
|
if(streams.ContainsKey(fs))
|
|||
|
{
|
|||
|
var strmItem = streams[fs];
|
|||
|
|
|||
|
if(strmItem.StreamList.Count == 0)
|
|||
|
{
|
|||
|
strmItem.Destroy.Invoke();
|
|||
|
}
|
|||
|
}
|
|||
|
mtx.ReleaseMutex();
|
|||
|
}
|
|||
|
|
|||
|
public override async Task DeleteDirectoryAsync(UnixPath path, CancellationToken token = default)
|
|||
|
{
|
|||
|
|
|||
|
var fs=await OpenTillAsync(path,token);
|
|||
|
await fs.DeleteDirectoryAsync(Last(path),token);
|
|||
|
CloseFileSystemIfNoStreams(fs);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
public override async Task DeleteFileAsync(UnixPath path, CancellationToken token = default)
|
|||
|
{
|
|||
|
|
|||
|
var fs=await OpenTillAsync(path,token);
|
|||
|
await fs.DeleteFileAsync(Last(path),token);
|
|||
|
CloseFileSystemIfNoStreams(fs);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
public override async Task<bool> DirectoryExistsAsync(UnixPath path, CancellationToken token = default)
|
|||
|
{
|
|||
|
|
|||
|
var fs=await OpenTillAsync(path,token);
|
|||
|
var res=await fs.DirectoryExistsAsync(Last(path));
|
|||
|
CloseFileSystemIfNoStreams(fs);
|
|||
|
|
|||
|
return res;
|
|||
|
}
|
|||
|
|
|||
|
public override async IAsyncEnumerable<UnixPath> EnumerateDirectoriesAsync(UnixPath path, [EnumeratorCancellation] CancellationToken token = default)
|
|||
|
{
|
|||
|
|
|||
|
|
|||
|
var fs=await OpenTillAsync(path,token);
|
|||
|
using(var reg= token.Register(()=>{
|
|||
|
CloseFileSystemIfNoStreams(fs);
|
|||
|
})){
|
|||
|
await foreach(var item in fs.EnumerateDirectoriesAsync(Last(path)))
|
|||
|
{
|
|||
|
yield return item;
|
|||
|
}
|
|||
|
CloseFileSystemIfNoStreams(fs);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
public override async IAsyncEnumerable<UnixPath> EnumerateFilesAsync(UnixPath path, [EnumeratorCancellation] CancellationToken token = default)
|
|||
|
{
|
|||
|
var fs=await OpenTillAsync(path,token);
|
|||
|
using(var reg= token.Register(()=>{
|
|||
|
CloseFileSystemIfNoStreams(fs);
|
|||
|
})){
|
|||
|
await foreach(var item in fs.EnumerateFilesAsync(Last(path)))
|
|||
|
{
|
|||
|
yield return item;
|
|||
|
}
|
|||
|
CloseFileSystemIfNoStreams(fs);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
public override async Task<bool> FileExistsAsync(UnixPath path, CancellationToken token = default)
|
|||
|
{
|
|||
|
|
|||
|
var fs=await OpenTillAsync(path,token);
|
|||
|
bool res = await fs.FileExistsAsync(Last(path),token);
|
|||
|
|
|||
|
CloseFileSystemIfNoStreams(fs);
|
|||
|
return res;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
public override async Task<DateTime> GetCreationTimeAsync(UnixPath path, CancellationToken token = default)
|
|||
|
{
|
|||
|
|
|||
|
var fs=await OpenTillAsync(path,token);
|
|||
|
DateTime res = await fs.GetCreationTimeAsync(Last(path),token);
|
|||
|
|
|||
|
CloseFileSystemIfNoStreams(fs);
|
|||
|
return res;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
public override async Task<DateTime> GetLastAccessTimeAsync(UnixPath path, CancellationToken token = default)
|
|||
|
{
|
|||
|
|
|||
|
var fs=await OpenTillAsync(path,token);
|
|||
|
DateTime res = await fs.GetLastAccessTimeAsync(Last(path),token);
|
|||
|
|
|||
|
CloseFileSystemIfNoStreams(fs);
|
|||
|
return res;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
public override async Task<DateTime> GetLastWriteTimeAsync(UnixPath path, CancellationToken token = default)
|
|||
|
{
|
|||
|
|
|||
|
var fs=await OpenTillAsync(path,token);
|
|||
|
DateTime res = await fs.GetLastWriteTimeAsync(Last(path),token);
|
|||
|
|
|||
|
CloseFileSystemIfNoStreams(fs);
|
|||
|
return res;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
public override async Task MoveDirectoryAsync(UnixPath src, UnixPath dest)
|
|||
|
{
|
|||
|
if(ExceptLast(src).AsText == ExceptLast(dest).AsText)
|
|||
|
{
|
|||
|
|
|||
|
var fs=await OpenTillAsync(src);
|
|||
|
await fs.MoveDirectoryAsync(Last(src),Last(dest));
|
|||
|
CloseFileSystemIfNoStreams(fs);
|
|||
|
|
|||
|
}else{
|
|||
|
var fs=await OpenTillAsync(src);
|
|||
|
var fs2=await OpenTillAsync(dest);
|
|||
|
await fs.MoveDirectoryAsync(fs2,Last(src),Last(dest));
|
|||
|
CloseFileSystemIfNoStreams(fs);
|
|||
|
CloseFileSystemIfNoStreams(fs2);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
public override async Task MoveFileAsync(UnixPath src, UnixPath dest)
|
|||
|
{
|
|||
|
if(ExceptLast(src).AsText == ExceptLast(dest).AsText)
|
|||
|
{
|
|||
|
|
|||
|
var fs=await OpenTillAsync(src);
|
|||
|
await fs.MoveFileAsync(Last(src),Last(dest));
|
|||
|
CloseFileSystemIfNoStreams(fs);
|
|||
|
|
|||
|
}else{
|
|||
|
var fs=await OpenTillAsync(src);
|
|||
|
var fs2=await OpenTillAsync(dest);
|
|||
|
await fs.MoveFileAsync(fs2,Last(src),Last(dest));
|
|||
|
CloseFileSystemIfNoStreams(fs);
|
|||
|
CloseFileSystemIfNoStreams(fs2);
|
|||
|
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public override async Task<Stream> OpenAsync(UnixPath path, FileMode mode, FileAccess access, FileShare share, CancellationToken token = default)
|
|||
|
{
|
|||
|
var fs = await OpenTillAsync(path,token);
|
|||
|
using(var t = token.Register(()=>{CloseFileSystemIfNoStreams(fs);}))
|
|||
|
{
|
|||
|
return await OpenStreamOnBehalf(fs,Last(path),mode,access,share,token);
|
|||
|
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public override async Task SetCreationTimeAsync(UnixPath path, DateTime time, CancellationToken token = default)
|
|||
|
{
|
|||
|
var fs=await OpenTillAsync(path);
|
|||
|
await fs.SetCreationTimeAsync(Last(path),time,token);
|
|||
|
CloseFileSystemIfNoStreams(fs);
|
|||
|
}
|
|||
|
|
|||
|
public override async Task SetLastAccessTimeAsync(UnixPath path, DateTime time, CancellationToken token = default)
|
|||
|
{
|
|||
|
var fs=await OpenTillAsync(path);
|
|||
|
await fs.SetLastAccessTimeAsync(Last(path),time,token);
|
|||
|
CloseFileSystemIfNoStreams(fs);
|
|||
|
}
|
|||
|
public override async Task SetAttributesAsync(UnixPath path, FileAttributes attributes, CancellationToken token = default)
|
|||
|
{
|
|||
|
var fs=await OpenTillAsync(path);
|
|||
|
await fs.SetAttributesAsync(Last(path),attributes,token);
|
|||
|
CloseFileSystemIfNoStreams(fs);
|
|||
|
}
|
|||
|
public override bool CanWatch(UnixPath path)
|
|||
|
{
|
|||
|
return false;
|
|||
|
}
|
|||
|
public override async Task<UnixPath> ReadLinkAsync(UnixPath file, CancellationToken token = default)
|
|||
|
{
|
|||
|
|
|||
|
var fs=await OpenTillAsync(file);
|
|||
|
var rlink=await fs.ReadLinkAsync(Last(file));
|
|||
|
CloseFileSystemIfNoStreams(fs);
|
|||
|
return rlink;
|
|||
|
}
|
|||
|
public override async Task<bool> SameFileSystemAsync(UnixPath src, UnixPath dest, CancellationToken token = default)
|
|||
|
{
|
|||
|
return await Task.FromResult(ExceptLast(src).AsText == ExceptLast(dest).AsText);
|
|||
|
}
|
|||
|
public override Task<bool> SymlinkExistsAsync(UnixPath file, CancellationToken token = default)
|
|||
|
{
|
|||
|
return base.SymlinkExistsAsync(file, token);
|
|||
|
}
|
|||
|
public override async Task<FileAttributes> GetAttributesAsync(UnixPath path, CancellationToken token = default)
|
|||
|
{
|
|||
|
var fs=await OpenTillAsync(path);
|
|||
|
var res=await fs.GetAttributesAsync(Last(path));
|
|||
|
CloseFileSystemIfNoStreams(fs);
|
|||
|
return res;
|
|||
|
}
|
|||
|
public override async Task CreateSymlinkAsync(UnixPath src, UnixPath dest, CancellationToken token = default)
|
|||
|
{
|
|||
|
if(ExceptLast(src).AsText != ExceptLast(dest).AsText) return;
|
|||
|
var fs=await OpenTillAsync(src);
|
|||
|
await fs.CreateSymlinkAsync(Last(src),Last(dest));
|
|||
|
CloseFileSystemIfNoStreams(fs);
|
|||
|
}
|
|||
|
public override async Task CreateHardlinkAsync(UnixPath src, UnixPath dest, CancellationToken token = default)
|
|||
|
{
|
|||
|
if(ExceptLast(src).AsText != ExceptLast(dest).AsText) return;
|
|||
|
var fs=await OpenTillAsync(src);
|
|||
|
await fs.CreateHardlinkAsync(Last(src),Last(dest));
|
|||
|
CloseFileSystemIfNoStreams(fs);
|
|||
|
}
|
|||
|
public override bool CanHandleSymlinks(UnixPath path)
|
|||
|
{
|
|||
|
var fs=Task.Run<IVirtualFilesystem>(async()=>await OpenTillAsync(path)).GetAwaiter().GetResult();
|
|||
|
var can=fs.CanHandleSymlinks(Last(path));
|
|||
|
CloseFileSystemIfNoStreams(fs);
|
|||
|
return can;
|
|||
|
}
|
|||
|
public override async Task SetLastWriteTimeAsync(UnixPath path, DateTime time, CancellationToken token = default)
|
|||
|
{
|
|||
|
var fs=await OpenTillAsync(path);
|
|||
|
await fs.SetLastWriteTimeAsync(Last(path),time,token);
|
|||
|
CloseFileSystemIfNoStreams(fs);
|
|||
|
}
|
|||
|
}
|
|||
|
*/
|
|||
|
public class Action2Event
|
|||
|
{
|
|||
|
public Action2Event(Action a)
|
|||
|
{
|
|||
|
Event+=a;
|
|||
|
}
|
|||
|
public void Add(Action a)
|
|||
|
{
|
|||
|
Event+=a;
|
|||
|
}
|
|||
|
public void Invoke()
|
|||
|
{
|
|||
|
Event?.Invoke();
|
|||
|
}
|
|||
|
public event Action Event;
|
|||
|
public static implicit operator Action2Event(Action a)
|
|||
|
{
|
|||
|
return new Action2Event(a);
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
}
|