XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

XFESystem

【Rust】XFE系统,基于Rust和汇编

公开
关注 0 Fork 0 Star 0
UTF-8
using System.Text;
using XFESystem.Core;

namespace XFESystem.Storage;

public sealed class MountedWorkspace : IWorkspaceBinaryStorage
{
    public const string DisksPath="X:\\Disks";
    readonly object gate=new();
    readonly IWorkspaceBinaryStorage workspace;
    readonly List<MountedVolume> volumes=[];
    readonly List<string> unavailable=[];
    readonly string reportPath;
    public bool IsPersistent=>workspace.IsPersistent;
    public MountedWorkspace(IWorkspaceBinaryStorage workspace,IEnumerable<IPhysicalDisk> disks,Action<string>? trace=null)
    {
        this.workspace=workspace;
        reportPath=DisksPath+"\\MountedVolumes.txt";
        for(int suffix=1;workspace.Exists(reportPath);suffix++)reportPath=DisksPath+"\\MountedVolumes-"+suffix+".txt";
        foreach(var disk in disks)
        {
            try
            {
                var partitions=DiskPartitions.Read(disk);
                foreach(var partition in partitions)
                {
                    string root=DisksPath+"\\"+disk.Name+"P"+partition.Number;
                    if(workspace.Exists(root)){unavailable.Add(root+": existing workspace path was preserved");continue;}
                    try
                    {
                        var volume=new MountedVolume(disk,partition,root);volumes.Add(volume);
                        trace?.Invoke("XFE_VOLUME_MOUNT|"+root+"|FS|"+volume.FileSystem+"|Writable|"+(volume.Writable?1:0)+"|Label|"+volume.Label);
                    }
                    catch(Exception error)when(error is IOException or ArgumentException or NotSupportedException or OverflowException or UnauthorizedAccessException)
                    {unavailable.Add(root+": "+error.Message);trace?.Invoke("XFE_VOLUME_UNAVAILABLE|"+root+"|"+error.GetType().Name);}
                }
            }
            catch(Exception error)when(error is IOException or ArgumentException or OverflowException)
            {unavailable.Add(disk.Name+": "+error.Message);trace?.Invoke("XFE_DISK_UNAVAILABLE|"+disk.Name+"|"+error.GetType().Name);}
        }
    }
    static bool Is(string a,string b)=>a.Equals(b,StringComparison.OrdinalIgnoreCase);
    static bool Under(string path,string root)=>Is(path,root)||path.StartsWith(root+"\\",StringComparison.OrdinalIgnoreCase);
    MountedVolume? Volume(string path)=>volumes.FirstOrDefault(v=>Under(path,v.Path));
    string ReportPath=>reportPath;
    string Report()=>"Mounted disks\n"+string.Join('\n',volumes.Select(v=>v.Path+" | "+v.FileSystem+" | "+v.Label+" | "+(v.Writable?"Read/write":"Read-only: "+v.ReadOnlyReason)))+
        "\n"+string.Join('\n',unavailable)+"\n";
    void Protect(string path){if(Is(path,DisksPath)||Is(path,ReportPath))throw new IOException("The disk list is managed by the system.");}
    public bool Exists(string path)
    {path=SystemPaths.Normalize(path);lock(gate)return Is(path,DisksPath)||Is(path,ReportPath)||(Volume(path)is{}v?v.Exists(path):workspace.Exists(path));}
    public WorkspaceEntry[] List(string path)
    {
        path=SystemPaths.Normalize(path);lock(gate)
        {
            if(Volume(path)is{}v)return v.List(path);
            var entries=workspace.Exists(path)?workspace.List(path).ToList():Is(path,DisksPath)?[]:throw new DirectoryNotFoundException(path);
            if(Is(path,SystemPaths.Root)&&!entries.Any(e=>Is(e.Path,DisksPath)))entries.Add(new(DisksPath,true,0));
            if(Is(path,DisksPath))
            {entries.AddRange(volumes.Select(v=>new WorkspaceEntry(v.Path,true,0)));if(!entries.Any(e=>Is(e.Path,ReportPath)))entries.Add(new(ReportPath,false,Encoding.UTF8.GetByteCount(Report())));}
            return entries.OrderBy(e=>!e.IsDirectory).ThenBy(e=>e.Name,StringComparer.OrdinalIgnoreCase).ToArray();
        }
    }
    public string ReadText(string path)
    {path=SystemPaths.Normalize(path);lock(gate)return Is(path,ReportPath)?Report():Volume(path)is{}v?v.ReadText(path):workspace.ReadText(path);}
    public byte[] ReadBytes(string path)
    {path=SystemPaths.Normalize(path);lock(gate)return Is(path,ReportPath)?Encoding.UTF8.GetBytes(Report()):Volume(path)is{}v?v.ReadBytes(path):workspace.ReadBytes(path);}
    public void WriteText(string path,string content)
    {path=SystemPaths.Normalize(path);lock(gate){Protect(path);if(Volume(path)is{}v)v.WriteText(path,content);else workspace.WriteText(path,content);}}
    public void WriteBytes(string path,ReadOnlySpan<byte> content)
    {path=SystemPaths.Normalize(path);lock(gate){Protect(path);if(Volume(path)is{}v)v.WriteBytes(path,content);else workspace.WriteBytes(path,content);}}
    public void CreateDirectory(string path)
    {path=SystemPaths.Normalize(path);lock(gate){if(Is(path,DisksPath))return;Protect(path);if(Volume(path)is{}v)v.CreateDirectory(path);else workspace.CreateDirectory(path);}}
    public void Delete(string path)
    {path=SystemPaths.Normalize(path);lock(gate){Protect(path);if(Volume(path)is{}v)v.Delete(path);else workspace.Delete(path);}}
    public void Move(string source,string destination)
    {
        source=SystemPaths.Normalize(source);destination=SystemPaths.Normalize(destination);lock(gate)
        {
            Protect(source);Protect(destination);var from=Volume(source);var to=Volume(destination);
            if(from==to){if(from!=null)from.Move(source,destination);else workspace.Move(source,destination);return;}
            if(Exists(destination))throw new IOException("Destination already exists.");
            // Cross-volume files commit and verify the destination before the
            // source is removed. Directory trees require explicit copy steps.
            byte[] bytes=ReadBytes(source);WriteBytes(destination,bytes);
            if(!ReadBytes(destination).AsSpan().SequenceEqual(bytes))throw new IOException("Cross-volume copy verification failed; source was preserved.");
            Delete(source);
        }
    }
}