// // Copyright (c) 2008-2011, Kenneth Bell // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the "Software"), // to deal in the Software without restriction, including without limitation // the rights to use, copy, modify, merge, publish, distribute, sublicense, // and/or sell copies of the Software, and to permit persons to whom the // Software is furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. // using System; using System.Collections.Generic; using DiscUtils.Streams; namespace DiscUtils.Partitions { /// /// Builds a stream with the contents of a BIOS partitioned disk. /// /// /// This class assembles a disk image dynamically in memory. The /// constructed stream will read data from the partition content /// streams only when a client of this class tries to read from /// that partition. /// public class BiosPartitionedDiskBuilder : StreamBuilder { private Geometry _biosGeometry; private readonly SparseMemoryStream _bootSectors; private readonly long _capacity; private readonly Dictionary _partitionContents; /// /// Initializes a new instance of the BiosPartitionedDiskBuilder class. /// /// The capacity of the disk (in bytes). /// The BIOS geometry of the disk. public BiosPartitionedDiskBuilder(long capacity, Geometry biosGeometry) { _capacity = capacity; _biosGeometry = biosGeometry; _bootSectors = new SparseMemoryStream(); _bootSectors.SetLength(capacity); PartitionTable = BiosPartitionTable.Initialize(_bootSectors, _biosGeometry); _partitionContents = new Dictionary(); } /// /// Initializes a new instance of the BiosPartitionedDiskBuilder class. /// /// The capacity of the disk (in bytes). /// The boot sector(s) of the disk. /// The BIOS geometry of the disk. [Obsolete("Use the variant that takes VirtualDisk, this method breaks for disks with extended partitions", false )] public BiosPartitionedDiskBuilder(long capacity, byte[] bootSectors, Geometry biosGeometry) { if (bootSectors == null) { throw new ArgumentNullException(nameof(bootSectors)); } _capacity = capacity; _biosGeometry = biosGeometry; _bootSectors = new SparseMemoryStream(); _bootSectors.SetLength(capacity); _bootSectors.Write(bootSectors, 0, bootSectors.Length); PartitionTable = new BiosPartitionTable(_bootSectors, biosGeometry); _partitionContents = new Dictionary(); } /// /// Initializes a new instance of the BiosPartitionedDiskBuilder class by /// cloning the partition structure of a source disk. /// /// The disk to clone. public BiosPartitionedDiskBuilder(VirtualDisk sourceDisk) { if (sourceDisk == null) { throw new ArgumentNullException(nameof(sourceDisk)); } _capacity = sourceDisk.Capacity; _biosGeometry = sourceDisk.BiosGeometry; _bootSectors = new SparseMemoryStream(); _bootSectors.SetLength(_capacity); foreach (StreamExtent extent in new BiosPartitionTable(sourceDisk).GetMetadataDiskExtents()) { sourceDisk.Content.Position = extent.Start; byte[] buffer = StreamUtilities.ReadExact(sourceDisk.Content, (int)extent.Length); _bootSectors.Position = extent.Start; _bootSectors.Write(buffer, 0, buffer.Length); } PartitionTable = new BiosPartitionTable(_bootSectors, _biosGeometry); _partitionContents = new Dictionary(); } /// /// Gets the partition table in the disk. /// public BiosPartitionTable PartitionTable { get; } /// /// Sets a stream representing the content of a partition in the partition table. /// /// The index of the partition. /// The stream with the contents of the partition. public void SetPartitionContent(int index, SparseStream stream) { _partitionContents[index] = new BuilderSparseStreamExtent(PartitionTable[index].FirstSector * Sizes.Sector, stream); } /// /// Updates the CHS fields in partition records to reflect a new BIOS geometry. /// /// The disk's new BIOS geometry. /// The partitions are not relocated to a cylinder boundary, just the CHS fields are updated on the /// assumption the LBA fields are definitive. public void UpdateBiosGeometry(Geometry geometry) { PartitionTable.UpdateBiosGeometry(geometry); _biosGeometry = geometry; } protected override List FixExtents(out long totalLength) { totalLength = _capacity; List extents = new List(); foreach (StreamExtent extent in PartitionTable.GetMetadataDiskExtents()) { _bootSectors.Position = extent.Start; byte[] buffer = StreamUtilities.ReadExact(_bootSectors, (int)extent.Length); extents.Add(new BuilderBufferExtent(extent.Start, buffer)); } extents.AddRange(_partitionContents.Values); return extents; } } }