namespace XFESystem.Storage; // Every seek and unaligned metadata write remains inside one validated // partition. A filesystem never receives a whole-device stream. public sealed class PartitionStream : Stream { readonly IPhysicalDisk disk; readonly DiskPartition partition; readonly bool writable; long position; public PartitionStream(IPhysicalDisk disk,DiskPartition partition,bool writable) { this.disk=disk;this.partition=partition;this.writable=writable&&disk.CanWrite&&partition.DataPartition; if(partition.Start>=disk.SectorCount||partition.Count==0||partition.Count>disk.SectorCount-partition.Start||partition.Count>(ulong)long.MaxValue/512)throw new IOException("Invalid partition stream extent."); } public override bool CanRead=>true; public override bool CanSeek=>true; public override bool CanWrite=>writable; public override long Length=>checked((long)partition.Count*512); public override long Position {get=>position;set{if(value<0||value>Length)throw new IOException("Seek leaves partition.");position=value;}} public override long Seek(long offset,SeekOrigin origin) {Position=checked((origin switch {SeekOrigin.Begin=>0L,SeekOrigin.Current=>position,SeekOrigin.End=>Length,_=>throw new ArgumentException("Invalid seek origin.")})+offset);return position;} public override void Flush()=>disk.Flush(); public override void SetLength(long value)=>throw new NotSupportedException("Partition resizing is not supported."); public override int Read(byte[] buffer,int offset,int count)=>Read(buffer.AsSpan(offset,count)); public override int Read(Span buffer) { int total=(int)Math.Min(buffer.Length,Length-position),done=0; while(doneWrite(buffer.AsSpan(offset,count)); public override void Write(ReadOnlySpan buffer) { if(!writable)throw new UnauthorizedAccessException("This volume is read-only."); if(buffer.Length>Length-position)throw new IOException("Write leaves partition."); while(!buffer.IsEmpty) { int skip=(int)(position&511),copy=Math.Min(buffer.Length,32768-skip),bytes=(skip+copy+511)&~511; byte[] sectors=new byte[bytes];ulong lba=partition.Start+(ulong)position/512; if(skip!=0||copy!=bytes)disk.Read(lba,sectors); buffer[..copy].CopyTo(sectors.AsSpan(skip));disk.AuthorizePartition(partition.Start,partition.Count);disk.Write(lba,sectors); position+=copy;buffer=buffer[copy..]; } } }