Files
duplicati/proprietary/DiskImage/Partition/Unknown.cs
T

162 lines
5.5 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Duplicati.Proprietary.DiskImage.Disk;
namespace Duplicati.Proprietary.DiskImage.Partition;
public class UnknownPartition : IPartition
{
public IPartitionTable PartitionTable { get; }
public int PartitionNumber => 1;
public PartitionType Type => PartitionType.Unknown;
public long StartOffset => 0;
public long Size { get; }
public string? Name => null;
public FileSystemType FilesystemType => FileSystemType.Unknown;
public Guid? VolumeGuid => null;
public UnknownPartition(IPartitionTable partitionTable, long size)
{
PartitionTable = partitionTable;
Size = size;
}
public async Task<Stream> OpenReadAsync(CancellationToken cancellationToken)
{
if (PartitionTable.RawDisk == null)
throw new InvalidOperationException("No raw disk available for reading partition data.");
return await PartitionTable.RawDisk.ReadBytesAsync(StartOffset, (int)Size, cancellationToken).ConfigureAwait(false);
}
public Task<Stream> OpenWriteAsync(CancellationToken cancellationToken)
{
if (PartitionTable.RawDisk == null)
throw new InvalidOperationException("No raw disk available for writing partition data.");
return Task.FromResult<Stream>(new PartitionWriteStream(PartitionTable.RawDisk, StartOffset, Size));
}
public void Dispose()
{
// Nothing to dispose here
}
/// <summary>
/// A stream that writes data to a partition on the raw disk.
/// </summary>
private class PartitionWriteStream : Stream
{
private readonly IRawDisk _disk;
private readonly long _startOffset;
private readonly long _maxSize;
private readonly MemoryStream _buffer;
private bool _disposed = false;
public PartitionWriteStream(IRawDisk disk, long startOffset, long maxSize)
{
_disk = disk;
_startOffset = startOffset;
_maxSize = maxSize;
_buffer = new MemoryStream();
}
public override bool CanRead => false;
public override bool CanSeek => true;
public override bool CanWrite => true;
public override long Length => _buffer.Length;
public override long Position
{
get => _buffer.Position;
set => _buffer.Position = value;
}
public override void Flush() => _buffer.Flush();
public override int Read(byte[] buffer, int offset, int count) => throw new NotSupportedException();
public override long Seek(long offset, SeekOrigin origin) => _buffer.Seek(offset, origin);
public override void SetLength(long value)
{
if (value > _maxSize)
throw new IOException($"Cannot write beyond partition size of {_maxSize} bytes.");
_buffer.SetLength(value);
}
public override void Write(byte[] buffer, int offset, int count)
{
if (_buffer.Position + count > _maxSize)
throw new IOException($"Cannot write beyond partition size of {_maxSize} bytes.");
_buffer.Write(buffer, offset, count);
}
protected override void Dispose(bool disposing)
{
if (!_disposed)
{
if (disposing)
{
// Write all buffered data to disk
_buffer.Position = 0;
var data = _buffer.ToArray();
if (data.Length > 0)
{
_disk.WriteBytesAsync(_startOffset, data, CancellationToken.None).GetAwaiter().GetResult();
}
_buffer.Dispose();
}
_disposed = true;
}
base.Dispose(disposing);
}
}
}
public class UnknownPartitionTable : IPartitionTable
{
private readonly IRawDisk? m_rawDisk;
private bool m_disposed = false;
public IRawDisk? RawDisk => m_rawDisk;
public PartitionTableType TableType => PartitionTableType.Unknown;
public UnknownPartitionTable(IRawDisk? disk)
{
m_rawDisk = disk;
}
public async IAsyncEnumerable<IPartition> EnumeratePartitions([System.Runtime.CompilerServices.EnumeratorCancellation] CancellationToken cancellationToken)
{
var partition = await GetPartitionAsync(1, cancellationToken).ConfigureAwait(false);
if (partition != null)
yield return partition;
}
public Task<IPartition?> GetPartitionAsync(int partitionNumber, CancellationToken cancellationToken)
{
if (partitionNumber != 1 || m_rawDisk == null)
return Task.FromResult<IPartition?>(null);
return Task.FromResult<IPartition?>(new UnknownPartition(this, m_rawDisk.Size));
}
public Task<Stream> GetProtectiveMbrAsync(CancellationToken cancellationToken)
{
throw new NotSupportedException("Unknown partition table does not have a protective MBR.");
}
public Task<Stream> GetPartitionTableDataAsync(CancellationToken cancellationToken)
{
// For unknown partition tables, return empty data
// The disk will be treated as one big partition with unknown filesystem
return Task.FromResult<Stream>(new MemoryStream(Array.Empty<byte>(), writable: false));
}
public void Dispose()
{
if (m_disposed)
return;
m_disposed = true;
}
}