Initial commit - WPinternals 2.6

This commit is contained in:
Rene Lergner
2018-10-25 22:35:49 +02:00
commit 396ae57f05
483 changed files with 159677 additions and 0 deletions
@@ -0,0 +1,117 @@
//
// 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.
//
namespace DiscUtils.Partitions
{
using System.Collections.Generic;
using System.IO;
internal class BiosExtendedPartitionTable
{
private Stream _disk;
private uint _firstSector;
public BiosExtendedPartitionTable(Stream disk, uint firstSector)
{
_disk = disk;
_firstSector = firstSector;
}
public BiosPartitionRecord[] GetPartitions()
{
List<BiosPartitionRecord> result = new List<BiosPartitionRecord>();
uint partPos = _firstSector;
while (partPos != 0)
{
_disk.Position = ((long)partPos) * Utilities.SectorSize;
byte[] sector = Utilities.ReadFully(_disk, Utilities.SectorSize);
if (sector[510] != 0x55 || sector[511] != 0xAA)
{
throw new IOException("Invalid extended partition sector");
}
uint nextPartPos = 0;
for (int offset = 0x1BE; offset <= 0x1EE; offset += 0x10)
{
BiosPartitionRecord thisPart = new BiosPartitionRecord(sector, offset, partPos, -1);
if (thisPart.StartCylinder != 0 || thisPart.StartHead != 0 || thisPart.StartSector != 0)
{
if (thisPart.PartitionType != 0x05 && thisPart.PartitionType != 0x0F)
{
result.Add(thisPart);
}
else
{
nextPartPos = _firstSector + thisPart.LBAStart;
}
}
}
partPos = nextPartPos;
}
return result.ToArray();
}
/// <summary>
/// Gets all of the disk ranges containing partition table data.
/// </summary>
/// <returns>Set of stream extents, indicated as byte offset from the start of the disk.</returns>
public IEnumerable<StreamExtent> GetMetadataDiskExtents()
{
List<StreamExtent> extents = new List<StreamExtent>();
uint partPos = _firstSector;
while (partPos != 0)
{
extents.Add(new StreamExtent(((long)partPos) * Utilities.SectorSize, Utilities.SectorSize));
_disk.Position = ((long)partPos) * Utilities.SectorSize;
byte[] sector = Utilities.ReadFully(_disk, Utilities.SectorSize);
if (sector[510] != 0x55 || sector[511] != 0xAA)
{
throw new IOException("Invalid extended partition sector");
}
uint nextPartPos = 0;
for (int offset = 0x1BE; offset <= 0x1EE; offset += 0x10)
{
BiosPartitionRecord thisPart = new BiosPartitionRecord(sector, offset, partPos, -1);
if (thisPart.StartCylinder != 0 || thisPart.StartHead != 0 || thisPart.StartSector != 0)
{
if (thisPart.PartitionType == 0x05 || thisPart.PartitionType == 0x0F)
{
nextPartPos = _firstSector + thisPart.LBAStart;
}
}
}
partPos = nextPartPos;
}
return extents;
}
}
}
+136
View File
@@ -0,0 +1,136 @@
//
// 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.
//
namespace DiscUtils.Partitions
{
using System;
using System.IO;
/// <summary>
/// Provides access to partition records in a BIOS (MBR) partition table.
/// </summary>
public sealed class BiosPartitionInfo : PartitionInfo
{
private BiosPartitionRecord _record;
private BiosPartitionTable _table;
internal BiosPartitionInfo(BiosPartitionTable table, BiosPartitionRecord record)
{
_table = table;
_record = record;
}
/// <summary>
/// Gets the first sector of the partion (relative to start of disk) as a Logical Block Address.
/// </summary>
public override long FirstSector
{
get { return _record.LBAStartAbsolute; }
}
/// <summary>
/// Gets the last sector of the partion (relative to start of disk) as a Logical Block Address (inclusive).
/// </summary>
public override long LastSector
{
get { return _record.LBAStartAbsolute + _record.LBALength - 1; }
}
/// <summary>
/// Always returns <see cref="System.Guid"/>.Empty.
/// </summary>
public override Guid GuidType
{
get { return Guid.Empty; }
}
/// <summary>
/// Gets the type of the partition.
/// </summary>
public override byte BiosType
{
get { return _record.PartitionType; }
}
/// <summary>
/// Gets the type of the partition as a string.
/// </summary>
public override string TypeAsString
{
get { return _record.FriendlyPartitionType; }
}
/// <summary>
/// Gets a value indicating whether this partition is active (bootable).
/// </summary>
public bool IsActive
{
get { return _record.Status != 0; }
}
/// <summary>
/// Gets the start of the partition as a CHS address.
/// </summary>
public ChsAddress Start
{
get { return new ChsAddress(_record.StartCylinder, _record.StartHead, _record.StartSector); }
}
/// <summary>
/// Gets the end (inclusive) of the partition as a CHS address.
/// </summary>
public ChsAddress End
{
get { return new ChsAddress(_record.EndCylinder, _record.EndHead, _record.EndSector); }
}
/// <summary>
/// Gets the index of the partition in the primary partition table, or <c>-1</c> if not a primary partition.
/// </summary>
public int PrimaryIndex
{
get { return _record.Index; }
}
/// <summary>
/// Gets a value indicating whether the partition is a primary (rather than extended) partition.
/// </summary>
public bool IsPrimary
{
get { return PrimaryIndex >= 0; }
}
internal override PhysicalVolumeType VolumeType
{
get { return PhysicalVolumeType.BiosPartition; }
}
/// <summary>
/// Opens a stream to access the content of the partition.
/// </summary>
/// <returns>The new stream.</returns>
public override SparseStream Open()
{
return _table.Open(_record);
}
}
}
+166
View File
@@ -0,0 +1,166 @@
//
// 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.
//
namespace DiscUtils.Partitions
{
using System;
internal class BiosPartitionRecord : IComparable<BiosPartitionRecord>
{
private uint _lbaOffset;
private byte _status;
private ushort _startCylinder;
private byte _startHead;
private byte _startSector;
private byte _type;
private ushort _endCylinder;
private byte _endHead;
private byte _endSector;
private uint _lbaStart;
private uint _lbaLength;
private int _index;
public BiosPartitionRecord()
{
}
public BiosPartitionRecord(byte[] data, int offset, uint lbaOffset, int index)
{
_lbaOffset = lbaOffset;
_status = data[offset];
_startHead = data[offset + 1];
_startSector = (byte)(data[offset + 2] & 0x3F);
_startCylinder = (ushort)(data[offset + 3] | ((data[offset + 2] & 0xC0) << 2));
_type = data[offset + 4];
_endHead = data[offset + 5];
_endSector = (byte)(data[offset + 6] & 0x3F);
_endCylinder = (ushort)(data[offset + 7] | ((data[offset + 6] & 0xC0) << 2));
_lbaStart = Utilities.ToUInt32LittleEndian(data, offset + 8);
_lbaLength = Utilities.ToUInt32LittleEndian(data, offset + 12);
_index = index;
}
public bool IsValid
{
get
{
return _endHead != 0 || _endSector != 0 || _endCylinder != 0 || _lbaLength != 0;
}
}
public byte Status
{
get { return _status; }
set { _status = value; }
}
public ushort StartCylinder
{
get { return _startCylinder; }
set { _startCylinder = value; }
}
public byte StartHead
{
get { return _startHead; }
set { _startHead = value; }
}
public byte StartSector
{
get { return _startSector; }
set { _startSector = value; }
}
public byte PartitionType
{
get { return _type; }
set { _type = value; }
}
public string FriendlyPartitionType
{
get { return BiosPartitionTypes.ToString(_type); }
}
public ushort EndCylinder
{
get { return _endCylinder; }
set { _endCylinder = value; }
}
public byte EndHead
{
get { return _endHead; }
set { _endHead = value; }
}
public byte EndSector
{
get { return _endSector; }
set { _endSector = value; }
}
public uint LBAStart
{
get { return _lbaStart; }
set { _lbaStart = value; }
}
public uint LBALength
{
get { return _lbaLength; }
set { _lbaLength = value; }
}
public uint LBAStartAbsolute
{
get { return _lbaStart + _lbaOffset; }
}
public int Index
{
get { return _index; }
}
public int CompareTo(BiosPartitionRecord other)
{
return LBAStartAbsolute.CompareTo(other.LBAStartAbsolute);
}
internal void WriteTo(byte[] buffer, int offset)
{
buffer[offset] = _status;
buffer[offset + 1] = _startHead;
buffer[offset + 2] = (byte)((_startSector & 0x3F) | ((_startCylinder >> 2) & 0xC0));
buffer[offset + 3] = (byte)_startCylinder;
buffer[offset + 4] = _type;
buffer[offset + 5] = _endHead;
buffer[offset + 6] = (byte)((_endSector & 0x3F) | ((_endCylinder >> 2) & 0xC0));
buffer[offset + 7] = (byte)_endCylinder;
Utilities.WriteBytesLittleEndian((uint)_lbaStart, buffer, offset + 8);
Utilities.WriteBytesLittleEndian((uint)_lbaLength, buffer, offset + 12);
}
}
}
+712
View File
@@ -0,0 +1,712 @@
//
// 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.
//
namespace DiscUtils.Partitions
{
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Globalization;
using System.IO;
/// <summary>
/// Represents a BIOS (MBR) Partition Table.
/// </summary>
public sealed class BiosPartitionTable : PartitionTable
{
private Stream _diskData;
private Geometry _diskGeometry;
/// <summary>
/// Initializes a new instance of the BiosPartitionTable class.
/// </summary>
/// <param name="disk">The disk containing the partition table.</param>
public BiosPartitionTable(VirtualDisk disk)
{
Init(disk.Content, disk.BiosGeometry);
}
/// <summary>
/// Initializes a new instance of the BiosPartitionTable class.
/// </summary>
/// <param name="disk">The stream containing the disk data.</param>
/// <param name="diskGeometry">The geometry of the disk.</param>
public BiosPartitionTable(Stream disk, Geometry diskGeometry)
{
Init(disk, diskGeometry);
}
/// <summary>
/// Gets the GUID that uniquely identifies this disk, if supported (else returns <c>null</c>).
/// </summary>
public override Guid DiskGuid
{
get { return Guid.Empty; }
}
/// <summary>
/// Gets a collection of the partitions for storing Operating System file-systems.
/// </summary>
public ReadOnlyCollection<BiosPartitionInfo> BiosUserPartitions
{
get
{
List<BiosPartitionInfo> result = new List<BiosPartitionInfo>();
foreach (BiosPartitionRecord r in GetAllRecords())
{
if (r.IsValid)
{
result.Add(new BiosPartitionInfo(this, r));
}
}
return new ReadOnlyCollection<BiosPartitionInfo>(result);
}
}
/// <summary>
/// Gets a collection of the partitions for storing Operating System file-systems.
/// </summary>
public override ReadOnlyCollection<PartitionInfo> Partitions
{
get
{
List<PartitionInfo> result = new List<PartitionInfo>();
foreach (BiosPartitionRecord r in GetAllRecords())
{
if (r.IsValid)
{
result.Add(new BiosPartitionInfo(this, r));
}
}
return new ReadOnlyCollection<PartitionInfo>(result);
}
}
/// <summary>
/// Makes a best guess at the geometry of a disk.
/// </summary>
/// <param name="disk">String containing the disk image to detect the geometry from.</param>
/// <returns>The detected geometry.</returns>
public static Geometry DetectGeometry(Stream disk)
{
if (disk.Length >= Utilities.SectorSize)
{
disk.Position = 0;
byte[] bootSector = Utilities.ReadFully(disk, Utilities.SectorSize);
if (bootSector[510] == 0x55 && bootSector[511] == 0xAA)
{
byte maxHead = 0;
byte maxSector = 0;
foreach (var record in ReadPrimaryRecords(bootSector))
{
maxHead = Math.Max(maxHead, record.EndHead);
maxSector = Math.Max(maxSector, record.EndSector);
}
if (maxHead > 0 && maxSector > 0)
{
int cylSize = (maxHead + 1) * maxSector * 512;
return new Geometry((int)Utilities.Ceil(disk.Length, cylSize), maxHead + 1, maxSector);
}
}
}
return Geometry.FromCapacity(disk.Length);
}
/// <summary>
/// Indicates if a stream contains a valid partition table.
/// </summary>
/// <param name="disk">The stream to inspect.</param>
/// <returns><c>true</c> if the partition table is valid, else <c>false</c>.</returns>
public static bool IsValid(Stream disk)
{
if (disk.Length < Utilities.SectorSize)
{
return false;
}
disk.Position = 0;
byte[] bootSector = Utilities.ReadFully(disk, Utilities.SectorSize);
// Check for the 'bootable sector' marker
if (bootSector[510] != 0x55 || bootSector[511] != 0xAA)
{
return false;
}
List<StreamExtent> knownPartitions = new List<StreamExtent>();
foreach (var record in ReadPrimaryRecords(bootSector))
{
// If the partition extends beyond the end of the disk, this is probably an invalid partition table
if (record.LBALength != 0xFFFFFFFF && (record.LBAStart + (long)record.LBALength) * Sizes.Sector > disk.Length)
{
return false;
}
if (record.LBALength > 0)
{
StreamExtent[] thisPartitionExtents = new StreamExtent[] { new StreamExtent(record.LBAStart, record.LBALength) };
// If the partition intersects another partition, this is probably an invalid partition table
foreach (var overlap in StreamExtent.Intersect(knownPartitions, thisPartitionExtents))
{
return false;
}
knownPartitions = new List<StreamExtent>(StreamExtent.Union(knownPartitions, thisPartitionExtents));
}
}
return true;
}
/// <summary>
/// Creates a new partition table on a disk.
/// </summary>
/// <param name="disk">The disk to initialize.</param>
/// <returns>An object to access the newly created partition table.</returns>
public static BiosPartitionTable Initialize(VirtualDisk disk)
{
return Initialize(disk.Content, disk.BiosGeometry);
}
/// <summary>
/// Creates a new partition table on a disk containing a single partition.
/// </summary>
/// <param name="disk">The disk to initialize.</param>
/// <param name="type">The partition type for the single partition.</param>
/// <returns>An object to access the newly created partition table.</returns>
public static BiosPartitionTable Initialize(VirtualDisk disk, WellKnownPartitionType type)
{
BiosPartitionTable table = Initialize(disk.Content, disk.BiosGeometry);
table.Create(type, true);
return table;
}
/// <summary>
/// Creates a new partition table on a disk.
/// </summary>
/// <param name="disk">The stream containing the disk data.</param>
/// <param name="diskGeometry">The geometry of the disk.</param>
/// <returns>An object to access the newly created partition table.</returns>
public static BiosPartitionTable Initialize(Stream disk, Geometry diskGeometry)
{
Stream data = disk;
byte[] bootSector;
if (data.Length >= Utilities.SectorSize)
{
data.Position = 0;
bootSector = Utilities.ReadFully(data, Utilities.SectorSize);
}
else
{
bootSector = new byte[Utilities.SectorSize];
}
// Wipe all four 16-byte partition table entries
Array.Clear(bootSector, 0x01BE, 16 * 4);
// Marker bytes
bootSector[510] = 0x55;
bootSector[511] = 0xAA;
data.Position = 0;
data.Write(bootSector, 0, bootSector.Length);
return new BiosPartitionTable(disk, diskGeometry);
}
/// <summary>
/// Creates a new partition that encompasses the entire disk.
/// </summary>
/// <param name="type">The partition type.</param>
/// <param name="active">Whether the partition is active (bootable).</param>
/// <returns>The index of the partition.</returns>
/// <remarks>The partition table must be empty before this method is called,
/// otherwise IOException is thrown.</remarks>
public override int Create(WellKnownPartitionType type, bool active)
{
Geometry allocationGeometry = new Geometry(_diskData.Length, _diskGeometry.HeadsPerCylinder, _diskGeometry.SectorsPerTrack, _diskGeometry.BytesPerSector);
ChsAddress start = new ChsAddress(0, 1, 1);
ChsAddress last = allocationGeometry.LastSector;
long startLba = allocationGeometry.ToLogicalBlockAddress(start);
long lastLba = allocationGeometry.ToLogicalBlockAddress(last);
return CreatePrimaryByCylinder(0, allocationGeometry.Cylinders - 1, ConvertType(type, (lastLba - startLba) * Utilities.SectorSize), active);
}
/// <summary>
/// Creates a new primary partition with a target size.
/// </summary>
/// <param name="size">The target size (in bytes).</param>
/// <param name="type">The partition type.</param>
/// <param name="active">Whether the partition is active (bootable).</param>
/// <returns>The index of the new partition.</returns>
public override int Create(long size, WellKnownPartitionType type, bool active)
{
int cylinderCapacity = _diskGeometry.SectorsPerTrack * _diskGeometry.HeadsPerCylinder * _diskGeometry.BytesPerSector;
int numCylinders = (int)(size / cylinderCapacity);
int startCylinder = FindCylinderGap(numCylinders);
return CreatePrimaryByCylinder(startCylinder, startCylinder + numCylinders - 1, ConvertType(type, size), active);
}
/// <summary>
/// Creates a new aligned partition that encompasses the entire disk.
/// </summary>
/// <param name="type">The partition type.</param>
/// <param name="active">Whether the partition is active (bootable).</param>
/// <param name="alignment">The alignment (in bytes).</param>
/// <returns>The index of the partition.</returns>
/// <remarks>The partition table must be empty before this method is called,
/// otherwise IOException is thrown.</remarks>
/// <remarks>
/// Traditionally partitions were aligned to the physical structure of the underlying disk,
/// however with modern storage greater efficiency is acheived by aligning partitions on
/// large values that are a power of two.
/// </remarks>
public override int CreateAligned(WellKnownPartitionType type, bool active, int alignment)
{
Geometry allocationGeometry = new Geometry(_diskData.Length, _diskGeometry.HeadsPerCylinder, _diskGeometry.SectorsPerTrack, _diskGeometry.BytesPerSector);
ChsAddress start = new ChsAddress(0, 1, 1);
long startLba = Utilities.RoundUp(allocationGeometry.ToLogicalBlockAddress(start), alignment / _diskGeometry.BytesPerSector);
long lastLba = Utilities.RoundDown(_diskData.Length / _diskGeometry.BytesPerSector, alignment / _diskGeometry.BytesPerSector);
return CreatePrimaryBySector(startLba, lastLba - 1, ConvertType(type, (lastLba - startLba) * _diskGeometry.BytesPerSector), active);
}
/// <summary>
/// Creates a new aligned partition with a target size.
/// </summary>
/// <param name="size">The target size (in bytes).</param>
/// <param name="type">The partition type.</param>
/// <param name="active">Whether the partition is active (bootable).</param>
/// <param name="alignment">The alignment (in bytes).</param>
/// <returns>The index of the new partition.</returns>
/// <remarks>
/// Traditionally partitions were aligned to the physical structure of the underlying disk,
/// however with modern storage greater efficiency is achieved by aligning partitions on
/// large values that are a power of two.
/// </remarks>
public override int CreateAligned(long size, WellKnownPartitionType type, bool active, int alignment)
{
if (size < _diskGeometry.BytesPerSector)
{
throw new ArgumentOutOfRangeException("size", size, "size must be at least one sector");
}
if (alignment % _diskGeometry.BytesPerSector != 0)
{
throw new ArgumentException("Alignment is not a multiple of the sector size");
}
if (size % alignment != 0)
{
throw new ArgumentException("Size is not a multiple of the alignment");
}
long sectorLength = size / _diskGeometry.BytesPerSector;
long start = FindGap(size / _diskGeometry.BytesPerSector, alignment / _diskGeometry.BytesPerSector);
return CreatePrimaryBySector(start, start + sectorLength - 1, ConvertType(type, sectorLength * Utilities.SectorSize), active);
}
/// <summary>
/// Deletes a partition at a given index.
/// </summary>
/// <param name="index">The index of the partition.</param>
public override void Delete(int index)
{
WriteRecord(index, new BiosPartitionRecord());
}
/// <summary>
/// Creates a new Primary Partition that occupies whole cylinders, for best compatibility.
/// </summary>
/// <param name="first">The first cylinder to include in the partition (inclusive).</param>
/// <param name="last">The last cylinder to include in the partition (inclusive).</param>
/// <param name="type">The BIOS (MBR) type of the new partition.</param>
/// <param name="markActive">Whether to mark the partition active (bootable).</param>
/// <returns>The index of the new partition.</returns>
/// <remarks>If the cylinder 0 is given, the first track will not be used, to reserve space
/// for the meta-data at the start of the disk.</remarks>
public int CreatePrimaryByCylinder(int first, int last, byte type, bool markActive)
{
if (first < 0)
{
throw new ArgumentOutOfRangeException("first", first, "First cylinder must be Zero or greater");
}
if (last <= first)
{
throw new ArgumentException("Last cylinder must be greater than first");
}
long lbaStart = (first == 0) ? _diskGeometry.ToLogicalBlockAddress(0, 1, 1) : _diskGeometry.ToLogicalBlockAddress(first, 0, 1);
long lbaLast = _diskGeometry.ToLogicalBlockAddress(last, _diskGeometry.HeadsPerCylinder - 1, _diskGeometry.SectorsPerTrack);
return CreatePrimaryBySector(lbaStart, lbaLast, type, markActive);
}
/// <summary>
/// Creates a new Primary Partition, specified by Logical Block Addresses.
/// </summary>
/// <param name="first">The LBA address of the first sector (inclusive).</param>
/// <param name="last">The LBA address of the last sector (inclusive).</param>
/// <param name="type">The BIOS (MBR) type of the new partition.</param>
/// <param name="markActive">Whether to mark the partition active (bootable).</param>
/// <returns>The index of the new partition.</returns>
public int CreatePrimaryBySector(long first, long last, byte type, bool markActive)
{
if (first >= last)
{
throw new ArgumentException("The first sector in a partition must be before the last");
}
if ((last + 1) * _diskGeometry.BytesPerSector > _diskData.Length)
{
throw new ArgumentOutOfRangeException("last", last, "The last sector extends beyond the end of the disk");
}
BiosPartitionRecord[] existing = GetPrimaryRecords();
BiosPartitionRecord newRecord = new BiosPartitionRecord();
ChsAddress startAddr = _diskGeometry.ToChsAddress(first);
ChsAddress endAddr = _diskGeometry.ToChsAddress(last);
// Because C/H/S addresses can max out at lower values than the LBA values,
// the special tuple (1023, 254, 63) is used.
if (startAddr.Cylinder > 1023)
{
startAddr = new ChsAddress(1023, 254, 63);
}
if (endAddr.Cylinder > 1023)
{
endAddr = new ChsAddress(1023, 254, 63);
}
newRecord.StartCylinder = (ushort)startAddr.Cylinder;
newRecord.StartHead = (byte)startAddr.Head;
newRecord.StartSector = (byte)startAddr.Sector;
newRecord.EndCylinder = (ushort)endAddr.Cylinder;
newRecord.EndHead = (byte)endAddr.Head;
newRecord.EndSector = (byte)endAddr.Sector;
newRecord.LBAStart = (uint)first;
newRecord.LBALength = (uint)(last - first + 1);
newRecord.PartitionType = type;
newRecord.Status = (byte)(markActive ? 0x80 : 0x00);
// First check for overlap with existing partition...
foreach (var r in existing)
{
if (Utilities.RangesOverlap((uint)first, (uint)last + 1, r.LBAStartAbsolute, r.LBAStartAbsolute + r.LBALength))
{
throw new IOException("New partition overlaps with existing partition");
}
}
// Now look for empty partition
for (int i = 0; i < 4; ++i)
{
if (!existing[i].IsValid)
{
WriteRecord(i, newRecord);
return i;
}
}
throw new IOException("No primary partition slots available");
}
/// <summary>
/// Sets the active partition.
/// </summary>
/// <param name="index">The index of the primary partition to mark bootable, or <c>-1</c> for none.</param>
/// <remarks>The supplied index is the index within the primary partition, see <c>PrimaryIndex</c> on <c>BiosPartitionInfo</c>.</remarks>
public void SetActivePartition(int index)
{
List<BiosPartitionRecord> records = new List<BiosPartitionRecord>(GetPrimaryRecords());
for (int i = 0; i < records.Count; ++i)
{
records[i].Status = (i == index) ? (byte)0x80 : (byte)0x00;
WriteRecord(i, records[i]);
}
}
/// <summary>
/// Gets all of the disk ranges containing partition table metadata.
/// </summary>
/// <returns>Set of stream extents, indicated as byte offset from the start of the disk.</returns>
public IEnumerable<StreamExtent> GetMetadataDiskExtents()
{
List<StreamExtent> extents = new List<StreamExtent>();
extents.Add(new StreamExtent(0, Sizes.Sector));
foreach (BiosPartitionRecord primaryRecord in GetPrimaryRecords())
{
if (primaryRecord.IsValid)
{
if (IsExtendedPartition(primaryRecord))
{
extents.AddRange(new BiosExtendedPartitionTable(_diskData, primaryRecord.LBAStart).GetMetadataDiskExtents());
}
}
}
return extents;
}
/// <summary>
/// Updates the CHS fields in partition records to reflect a new BIOS geometry.
/// </summary>
/// <param name="geometry">The disk's new BIOS geometry.</param>
/// <remarks>The partitions are not relocated to a cylinder boundary, just the CHS fields are updated on the
/// assumption the LBA fields are definitive.</remarks>
public void UpdateBiosGeometry(Geometry geometry)
{
_diskData.Position = 0;
byte[] bootSector = Utilities.ReadFully(_diskData, Utilities.SectorSize);
BiosPartitionRecord[] records = ReadPrimaryRecords(bootSector);
for (int i = 0; i < records.Length; ++i)
{
BiosPartitionRecord record = records[i];
if (record.IsValid)
{
ChsAddress newStartAddress = geometry.ToChsAddress(record.LBAStartAbsolute);
if (newStartAddress.Cylinder > 1023)
{
newStartAddress = new ChsAddress(1023, geometry.HeadsPerCylinder - 1, geometry.SectorsPerTrack);
}
ChsAddress newEndAddress = geometry.ToChsAddress(record.LBAStartAbsolute + record.LBALength - 1);
if (newEndAddress.Cylinder > 1023)
{
newEndAddress = new ChsAddress(1023, geometry.HeadsPerCylinder - 1, geometry.SectorsPerTrack);
}
record.StartCylinder = (ushort)newStartAddress.Cylinder;
record.StartHead = (byte)newStartAddress.Head;
record.StartSector = (byte)newStartAddress.Sector;
record.EndCylinder = (ushort)newEndAddress.Cylinder;
record.EndHead = (byte)newEndAddress.Head;
record.EndSector = (byte)newEndAddress.Sector;
WriteRecord(i, record);
}
}
_diskGeometry = geometry;
}
internal SparseStream Open(BiosPartitionRecord record)
{
return new SubStream(_diskData, Ownership.None, ((long)record.LBAStartAbsolute) * _diskGeometry.BytesPerSector, ((long)record.LBALength) * _diskGeometry.BytesPerSector);
}
private static BiosPartitionRecord[] ReadPrimaryRecords(byte[] bootSector)
{
BiosPartitionRecord[] records = new BiosPartitionRecord[4];
for (int i = 0; i < 4; ++i)
{
records[i] = new BiosPartitionRecord(bootSector, 0x01BE + (i * 0x10), 0, i);
}
return records;
}
private static bool IsExtendedPartition(BiosPartitionRecord r)
{
return r.PartitionType == BiosPartitionTypes.Extended || r.PartitionType == BiosPartitionTypes.ExtendedLba;
}
private static byte ConvertType(WellKnownPartitionType type, long size)
{
switch (type)
{
case WellKnownPartitionType.WindowsFat:
if (size < 512 * Sizes.OneMiB)
{
return BiosPartitionTypes.Fat16;
}
else if (size < 1023 * (long)254 * 63 * 512)
{
// Max BIOS size
return BiosPartitionTypes.Fat32;
}
else
{
return BiosPartitionTypes.Fat32Lba;
}
case WellKnownPartitionType.WindowsNtfs:
return BiosPartitionTypes.Ntfs;
case WellKnownPartitionType.Linux:
return BiosPartitionTypes.LinuxNative;
case WellKnownPartitionType.LinuxSwap:
return BiosPartitionTypes.LinuxSwap;
case WellKnownPartitionType.LinuxLvm:
return BiosPartitionTypes.LinuxLvm;
default:
throw new ArgumentException(String.Format(CultureInfo.InvariantCulture, "Unrecognized partition type: '{0}'", type), "type");
}
}
private BiosPartitionRecord[] GetAllRecords()
{
List<BiosPartitionRecord> newList = new List<BiosPartitionRecord>();
foreach (BiosPartitionRecord primaryRecord in GetPrimaryRecords())
{
if (primaryRecord.IsValid)
{
if (IsExtendedPartition(primaryRecord))
{
newList.AddRange(GetExtendedRecords(primaryRecord));
}
else
{
newList.Add(primaryRecord);
}
}
}
return newList.ToArray();
}
private BiosPartitionRecord[] GetPrimaryRecords()
{
_diskData.Position = 0;
byte[] bootSector = Utilities.ReadFully(_diskData, Utilities.SectorSize);
return ReadPrimaryRecords(bootSector);
}
private BiosPartitionRecord[] GetExtendedRecords(BiosPartitionRecord r)
{
return new BiosExtendedPartitionTable(_diskData, r.LBAStart).GetPartitions();
}
private void WriteRecord(int i, BiosPartitionRecord newRecord)
{
_diskData.Position = 0;
byte[] bootSector = Utilities.ReadFully(_diskData, Utilities.SectorSize);
newRecord.WriteTo(bootSector, 0x01BE + (i * 16));
_diskData.Position = 0;
_diskData.Write(bootSector, 0, bootSector.Length);
}
private int FindCylinderGap(int numCylinders)
{
var list = Utilities.Filter<List<BiosPartitionRecord>, BiosPartitionRecord>(GetPrimaryRecords(), (r) => r.IsValid);
list.Sort();
int startCylinder = 0;
foreach (var r in list)
{
int existingStart = r.StartCylinder;
int existingEnd = r.EndCylinder;
// LBA can represent bigger disk locations than CHS, so assume the LBA to be definitive in the case where it
// appears the CHS address has been truncated.
if (r.LBAStart > _diskGeometry.ToLogicalBlockAddress(r.StartCylinder, r.StartHead, r.StartSector))
{
existingStart = _diskGeometry.ToChsAddress((int)r.LBAStart).Cylinder;
}
if (r.LBAStart + r.LBALength > _diskGeometry.ToLogicalBlockAddress(r.EndCylinder, r.EndHead, r.EndSector))
{
existingEnd = _diskGeometry.ToChsAddress((int)(r.LBAStart + r.LBALength)).Cylinder;
}
if (!Utilities.RangesOverlap(startCylinder, startCylinder + numCylinders - 1, existingStart, existingEnd))
{
break;
}
else
{
startCylinder = existingEnd + 1;
}
}
return startCylinder;
}
private long FindGap(long numSectors, long alignmentSectors)
{
var list = Utilities.Filter<List<BiosPartitionRecord>, BiosPartitionRecord>(GetPrimaryRecords(), (r) => r.IsValid);
list.Sort();
long startSector = Utilities.RoundUp(_diskGeometry.ToLogicalBlockAddress(0, 1, 1), alignmentSectors);
int idx = 0;
while (idx < list.Count)
{
var entry = list[idx];
while (idx < list.Count && startSector >= entry.LBAStartAbsolute + entry.LBALength)
{
idx++;
entry = list[idx];
}
if (Utilities.RangesOverlap(startSector, startSector + numSectors, entry.LBAStartAbsolute, entry.LBAStartAbsolute + entry.LBALength))
{
startSector = Utilities.RoundUp(entry.LBAStartAbsolute + entry.LBALength, alignmentSectors);
}
idx++;
}
if (_diskGeometry.TotalSectorsLong - startSector < numSectors)
{
throw new IOException(string.Format(CultureInfo.InvariantCulture, "Unable to find free space of {0} sectors", numSectors));
}
return startSector;
}
private void Init(Stream disk, Geometry diskGeometry)
{
_diskData = disk;
_diskGeometry = diskGeometry;
_diskData.Position = 0;
byte[] bootSector = Utilities.ReadFully(_diskData, Utilities.SectorSize);
if (bootSector[510] != 0x55 || bootSector[511] != 0xAA)
{
throw new IOException("Invalid boot sector - no magic number 0xAA55");
}
}
}
}
+157
View File
@@ -0,0 +1,157 @@
//
// 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.
//
namespace DiscUtils.Partitions
{
/// <summary>
/// Convenient access to well-known BIOS (MBR) Partition Types.
/// </summary>
public static class BiosPartitionTypes
{
/// <summary>
/// Microsoft FAT12 (fewer than 32,680 sectors in the volume).
/// </summary>
public const byte Fat12 = 0x01;
/// <summary>
/// Microsoft FAT16 (32,68065,535 sectors or 16 MB33 MB).
/// </summary>
public const byte Fat16Small = 0x04;
/// <summary>
/// Extended Partition (contains other partitions).
/// </summary>
public const byte Extended = 0x05;
/// <summary>
/// Microsoft BIGDOS FAT16 (33 MB4 GB).
/// </summary>
public const byte Fat16 = 0x06;
/// <summary>
/// Installable File System (NTFS).
/// </summary>
public const byte Ntfs = 0x07;
/// <summary>
/// Microsoft FAT32.
/// </summary>
public const byte Fat32 = 0x0B;
/// <summary>
/// Microsoft FAT32, accessed using Int13h BIOS LBA extensions.
/// </summary>
public const byte Fat32Lba = 0x0C;
/// <summary>
/// Microsoft BIGDOS FAT16, accessed using Int13h BIOS LBA extensions.
/// </summary>
public const byte Fat16Lba = 0x0E;
/// <summary>
/// Extended Partition (contains other partitions), accessed using Int13h BIOS LBA extensions.
/// </summary>
public const byte ExtendedLba = 0x0F;
/// <summary>
/// Windows Logical Disk Manager dynamic volume.
/// </summary>
public const byte WindowsDynamicVolume = 0x42;
/// <summary>
/// Linux Swap.
/// </summary>
public const byte LinuxSwap = 0x82;
/// <summary>
/// Linux Native (ext2 and friends).
/// </summary>
public const byte LinuxNative = 0x83;
/// <summary>
/// Linux Logical Volume Manager (LVM).
/// </summary>
public const byte LinuxLvm = 0x8E;
/// <summary>
/// GUID Partition Table (GPT) protective partition, fills entire disk.
/// </summary>
public const byte GptProtective = 0xEE;
/// <summary>
/// EFI System partition on an MBR disk.
/// </summary>
public const byte EfiSystem = 0xEF;
/// <summary>
/// Provides a string representation of some known BIOS partition types.
/// </summary>
/// <param name="type">The partition type to represent as a string.</param>
/// <returns>The string representation.</returns>
public static string ToString(byte type)
{
switch (type)
{
case 0x00: return "Unused";
case 0x01: return "FAT12";
case 0x02: return "XENIX root";
case 0x03: return "XENIX /usr";
case 0x04: return "FAT16 (<32M)";
case 0x05: return "Extended (non-LBA)";
case 0x06: return "FAT16 (>32M)";
case 0x07: return "IFS (NTFS or HPFS)";
case 0x0B: return "FAT32 (non-LBA)";
case 0x0C: return "FAT32 (LBA)";
case 0x0E: return "FAT16 (LBA)";
case 0x0F: return "Extended (LBA)";
case 0x11: return "Hidden FAT12";
case 0x12: return "Vendor Config/Recovery/Diagnostics";
case 0x14: return "Hidden FAT16 (<32M)";
case 0x16: return "Hidden FAT16 (>32M)";
case 0x17: return "Hidden IFS (NTFS or HPFS)";
case 0x1B: return "Hidden FAT32 (non-LBA)";
case 0x1C: return "Hidden FAT32 (LBA)";
case 0x1E: return "Hidden FAT16 (LBA)";
case 0x27: return "Windows Recovery Environment";
case 0x42: return "Windows Dynamic Volume";
case 0x80: return "Minix v1.1 - v1.4a";
case 0x81: return "Minix / Early Linux";
case 0x82: return "Linux Swap";
case 0x83: return "Linux Native";
case 0x84: return "Hibernation";
case 0x8E: return "Linux LVM";
case 0xA0: return "Laptop Hibernation";
case 0xA8: return "Mac OS-X";
case 0xAB: return "Mac OS-X Boot";
case 0xAF: return "Mac OS-X HFS";
case 0xC0: return "NTFT";
case 0xDE: return "Dell OEM";
case 0xEE: return "GPT Protective";
case 0xEF: return "EFI";
case 0xFB: return "VMware File System";
case 0xFC: return "VMware Swap";
case 0xFE: return "IBM OEM";
default: return "Unknown";
}
}
}
}
@@ -0,0 +1,169 @@
//
// 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.
//
namespace DiscUtils.Partitions
{
using System;
using System.Collections.Generic;
using System.IO;
/// <summary>
/// Builds a stream with the contents of a BIOS partitioned disk.
/// </summary>
/// <remarks>
/// 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.
/// </remarks>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1001:TypesThatOwnDisposableFieldsShouldBeDisposable", Justification = "SparseMemoryStream holds no resources")]
public class BiosPartitionedDiskBuilder : StreamBuilder
{
private long _capacity;
private BiosPartitionTable _partitionTable;
private Geometry _biosGeometry;
private SparseMemoryStream _bootSectors;
private Dictionary<int, BuilderExtent> _partitionContents;
/// <summary>
/// Initializes a new instance of the BiosPartitionedDiskBuilder class.
/// </summary>
/// <param name="capacity">The capacity of the disk (in bytes).</param>
/// <param name="biosGeometry">The BIOS geometry of the disk.</param>
public BiosPartitionedDiskBuilder(long capacity, Geometry biosGeometry)
{
_capacity = capacity;
_biosGeometry = biosGeometry;
_bootSectors = new SparseMemoryStream();
_bootSectors.SetLength(capacity);
_partitionTable = BiosPartitionTable.Initialize(_bootSectors, _biosGeometry);
_partitionContents = new Dictionary<int, BuilderExtent>();
}
/// <summary>
/// Initializes a new instance of the BiosPartitionedDiskBuilder class.
/// </summary>
/// <param name="capacity">The capacity of the disk (in bytes).</param>
/// <param name="bootSectors">The boot sector(s) of the disk.</param>
/// <param name="biosGeometry">The BIOS geometry of the disk.</param>
[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("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<int, BuilderExtent>();
}
/// <summary>
/// Initializes a new instance of the BiosPartitionedDiskBuilder class by
/// cloning the partition structure of a source disk.
/// </summary>
/// <param name="sourceDisk">The disk to clone.</param>
public BiosPartitionedDiskBuilder(VirtualDisk sourceDisk)
{
if (sourceDisk == null)
{
throw new ArgumentNullException("sourceDisk");
}
_capacity = sourceDisk.Capacity;
_biosGeometry = sourceDisk.BiosGeometry;
_bootSectors = new SparseMemoryStream();
_bootSectors.SetLength(_capacity);
foreach (var extent in new BiosPartitionTable(sourceDisk).GetMetadataDiskExtents())
{
sourceDisk.Content.Position = extent.Start;
byte[] buffer = Utilities.ReadFully(sourceDisk.Content, (int)extent.Length);
_bootSectors.Position = extent.Start;
_bootSectors.Write(buffer, 0, buffer.Length);
}
_partitionTable = new BiosPartitionTable(_bootSectors, _biosGeometry);
_partitionContents = new Dictionary<int, BuilderExtent>();
}
/// <summary>
/// Gets the partition table in the disk.
/// </summary>
public BiosPartitionTable PartitionTable
{
get { return _partitionTable; }
}
/// <summary>
/// Sets a stream representing the content of a partition in the partition table.
/// </summary>
/// <param name="index">The index of the partition.</param>
/// <param name="stream">The stream with the contents of the partition.</param>
public void SetPartitionContent(int index, SparseStream stream)
{
_partitionContents[index] = new BuilderSparseStreamExtent(_partitionTable[index].FirstSector * Sizes.Sector, stream);
}
/// <summary>
/// Updates the CHS fields in partition records to reflect a new BIOS geometry.
/// </summary>
/// <param name="geometry">The disk's new BIOS geometry.</param>
/// <remarks>The partitions are not relocated to a cylinder boundary, just the CHS fields are updated on the
/// assumption the LBA fields are definitive.</remarks>
public void UpdateBiosGeometry(Geometry geometry)
{
_partitionTable.UpdateBiosGeometry(geometry);
_biosGeometry = geometry;
}
internal override List<BuilderExtent> FixExtents(out long totalLength)
{
totalLength = _capacity;
List<BuilderExtent> extents = new List<BuilderExtent>();
foreach (var extent in _partitionTable.GetMetadataDiskExtents())
{
_bootSectors.Position = extent.Start;
byte[] buffer = Utilities.ReadFully(_bootSectors, (int)extent.Length);
extents.Add(new BuilderBufferExtent(extent.Start, buffer));
}
extents.AddRange(_partitionContents.Values);
return extents;
}
}
}
@@ -0,0 +1,55 @@
//
// 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.
//
namespace DiscUtils.Partitions
{
using System.IO;
[PartitionTableFactory]
internal sealed class DefaultPartitionTableFactory : PartitionTableFactory
{
public override bool DetectIsPartitioned(Stream s)
{
return BiosPartitionTable.IsValid(s);
}
public override PartitionTable DetectPartitionTable(VirtualDisk disk)
{
if (BiosPartitionTable.IsValid(disk.Content))
{
BiosPartitionTable table = new BiosPartitionTable(disk);
if (table.Count == 1 && table[0].BiosType == BiosPartitionTypes.GptProtective)
{
return new GuidPartitionTable(disk);
}
else
{
return table;
}
}
else
{
return null;
}
}
}
}
+112
View File
@@ -0,0 +1,112 @@
//
// 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.
//
namespace DiscUtils.Partitions
{
using System;
using System.Text;
internal class GptEntry : IComparable<GptEntry>
{
public Guid PartitionType;
public Guid Identity;
public long FirstUsedLogicalBlock;
public long LastUsedLogicalBlock;
public ulong Attributes;
public string Name;
public GptEntry()
{
PartitionType = Guid.Empty;
Identity = Guid.Empty;
Name = string.Empty;
}
public string FriendlyPartitionType
{
get
{
switch (PartitionType.ToString().ToUpperInvariant())
{
case "00000000-0000-0000-0000-000000000000": return "Unused";
case "024DEE41-33E7-11D3-9D69-0008C781F39F": return "MBR Partition Scheme";
case "C12A7328-F81F-11D2-BA4B-00A0C93EC93B": return "EFI System";
case "21686148-6449-6E6F-744E-656564454649": return "BIOS Boot";
case "E3C9E316-0B5C-4DB8-817D-F92DF00215AE": return "Microsoft Reserved";
case "EBD0A0A2-B9E5-4433-87C0-68B6B72699C7": return "Windows Basic Data";
case "5808C8AA-7E8F-42E0-85D2-E1E90434CFB3": return "Windows Logical Disk Manager Metadata";
case "AF9B60A0-1431-4F62-BC68-3311714A69AD": return "Windows Logical Disk Manager Data";
case "75894C1E-3AEB-11D3-B7C1-7B03A0000000": return "HP-UX Data";
case "E2A1E728-32E3-11D6-A682-7B03A0000000": return "HP-UX Service";
case "A19D880F-05FC-4D3B-A006-743F0F84911E": return "Linux RAID";
case "0657FD6D-A4AB-43C4-84E5-0933C84B4F4F": return "Linux Swap";
case "E6D6D379-F507-44C2-A23C-238F2A3DF928": return "Linux Logical Volume Manager";
case "83BD6B9D-7F41-11DC-BE0B-001560B84F0F": return "FreeBSD Boot";
case "516E7CB4-6ECF-11D6-8FF8-00022D09712B": return "FreeBSD Data";
case "516E7CB5-6ECF-11D6-8FF8-00022D09712B": return "FreeBSD Swap";
case "516E7CB6-6ECF-11D6-8FF8-00022D09712B": return "FreeBSD Unix File System";
case "516E7CB8-6ECF-11D6-8FF8-00022D09712B": return "FreeBSD Vinum volume manager";
case "516E7CBA-6ECF-11D6-8FF8-00022D09712B": return "FreeBSD ZFS";
case "48465300-0000-11AA-AA11-00306543ECAC": return "Mac OS X HFS+";
case "55465300-0000-11AA-AA11-00306543ECAC": return "Mac OS X UFS";
case "6A898CC3-1DD2-11B2-99A6-080020736631": return "Mac OS X ZFS";
case "52414944-0000-11AA-AA11-00306543ECAC": return "Mac OS X RAID";
case "52414944-5F4F-11AA-AA11-00306543ECAC": return "Mac OS X RAID, Offline";
case "426F6F74-0000-11AA-AA11-00306543ECAC": return "Mac OS X Boot";
case "4C616265-6C00-11AA-AA11-00306543ECAC": return "Mac OS X Label";
case "49F48D32-B10E-11DC-B99B-0019D1879648": return "NetBSD Swap";
case "49F48D5A-B10E-11DC-B99B-0019D1879648": return "NetBSD Fast File System";
case "49F48D82-B10E-11DC-B99B-0019D1879648": return "NetBSD Log-Structed File System";
case "49F48DAA-B10E-11DC-B99B-0019D1879648": return "NetBSD RAID";
case "2DB519C4-B10F-11DC-B99B-0019D1879648": return "NetBSD Concatenated";
case "2DB519EC-B10F-11DC-B99B-0019D1879648": return "NetBSD Encrypted";
default: return "Unknown";
}
}
}
public void ReadFrom(byte[] buffer, int offset)
{
PartitionType = Utilities.ToGuidLittleEndian(buffer, offset + 0);
Identity = Utilities.ToGuidLittleEndian(buffer, offset + 16);
FirstUsedLogicalBlock = Utilities.ToInt64LittleEndian(buffer, offset + 32);
LastUsedLogicalBlock = Utilities.ToInt64LittleEndian(buffer, offset + 40);
Attributes = Utilities.ToUInt64LittleEndian(buffer, offset + 48);
Name = Encoding.Unicode.GetString(buffer, offset + 56, 72).TrimEnd(new char[] { '\0' });
}
public void WriteTo(byte[] buffer, int offset)
{
Utilities.WriteBytesLittleEndian(PartitionType, buffer, offset + 0);
Utilities.WriteBytesLittleEndian(Identity, buffer, offset + 16);
Utilities.WriteBytesLittleEndian(FirstUsedLogicalBlock, buffer, offset + 32);
Utilities.WriteBytesLittleEndian(LastUsedLogicalBlock, buffer, offset + 40);
Utilities.WriteBytesLittleEndian(Attributes, buffer, offset + 48);
Encoding.Unicode.GetBytes(Name + new string('\0', 36), 0, 36, buffer, offset + 56);
}
public int CompareTo(GptEntry other)
{
return FirstUsedLogicalBlock.CompareTo(other.FirstUsedLogicalBlock);
}
}
}
+144
View File
@@ -0,0 +1,144 @@
//
// 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.
//
namespace DiscUtils.Partitions
{
using System;
internal class GptHeader
{
public const string GptSignature = "EFI PART";
public string Signature;
public uint Version;
public int HeaderSize;
public uint Crc;
public long HeaderLba;
public long AlternateHeaderLba;
public long FirstUsable;
public long LastUsable;
public Guid DiskGuid;
public long PartitionEntriesLba;
public uint PartitionEntryCount;
public int PartitionEntrySize;
public uint EntriesCrc;
public byte[] Buffer;
public GptHeader(int sectorSize)
{
Signature = GptSignature;
Version = 0x00010000;
HeaderSize = 92;
Buffer = new byte[sectorSize];
}
public GptHeader(GptHeader toCopy)
{
Signature = toCopy.Signature;
Version = toCopy.Version;
HeaderSize = toCopy.HeaderSize;
Crc = toCopy.Crc;
HeaderLba = toCopy.HeaderLba;
AlternateHeaderLba = toCopy.AlternateHeaderLba;
FirstUsable = toCopy.FirstUsable;
LastUsable = toCopy.LastUsable;
DiskGuid = toCopy.DiskGuid;
PartitionEntriesLba = toCopy.PartitionEntriesLba;
PartitionEntryCount = toCopy.PartitionEntryCount;
PartitionEntrySize = toCopy.PartitionEntrySize;
EntriesCrc = toCopy.EntriesCrc;
Buffer = new byte[toCopy.Buffer.Length];
Array.Copy(toCopy.Buffer, Buffer, Buffer.Length);
}
public bool ReadFrom(byte[] buffer, int offset)
{
Signature = Utilities.BytesToString(buffer, offset + 0, 8);
Version = Utilities.ToUInt32LittleEndian(buffer, offset + 8);
HeaderSize = Utilities.ToInt32LittleEndian(buffer, offset + 12);
Crc = Utilities.ToUInt32LittleEndian(buffer, offset + 16);
HeaderLba = Utilities.ToInt64LittleEndian(buffer, offset + 24);
AlternateHeaderLba = Utilities.ToInt64LittleEndian(buffer, offset + 32);
FirstUsable = Utilities.ToInt64LittleEndian(buffer, offset + 40);
LastUsable = Utilities.ToInt64LittleEndian(buffer, offset + 48);
DiskGuid = Utilities.ToGuidLittleEndian(buffer, offset + 56);
PartitionEntriesLba = Utilities.ToInt64LittleEndian(buffer, offset + 72);
PartitionEntryCount = Utilities.ToUInt32LittleEndian(buffer, offset + 80);
PartitionEntrySize = Utilities.ToInt32LittleEndian(buffer, offset + 84);
EntriesCrc = Utilities.ToUInt32LittleEndian(buffer, offset + 88);
// In case the header has new fields unknown to us, store the entire header
// as a byte array
Buffer = new byte[HeaderSize];
Array.Copy(buffer, offset, Buffer, 0, HeaderSize);
// Reject obviously invalid data
if (Signature != GptSignature || HeaderSize == 0)
{
return false;
}
return Crc == CalcCrc(buffer, offset, HeaderSize);
}
public void WriteTo(byte[] buffer, int offset)
{
// First, copy the cached header to allow for unknown fields
Array.Copy(Buffer, 0, buffer, offset, Buffer.Length);
// Next, write the fields
Utilities.StringToBytes(Signature, buffer, offset + 0, 8);
Utilities.WriteBytesLittleEndian(Version, buffer, offset + 8);
Utilities.WriteBytesLittleEndian(HeaderSize, buffer, offset + 12);
Utilities.WriteBytesLittleEndian((uint)0, buffer, offset + 16);
Utilities.WriteBytesLittleEndian(HeaderLba, buffer, offset + 24);
Utilities.WriteBytesLittleEndian(AlternateHeaderLba, buffer, offset + 32);
Utilities.WriteBytesLittleEndian(FirstUsable, buffer, offset + 40);
Utilities.WriteBytesLittleEndian(LastUsable, buffer, offset + 48);
Utilities.WriteBytesLittleEndian(DiskGuid, buffer, offset + 56);
Utilities.WriteBytesLittleEndian(PartitionEntriesLba, buffer, offset + 72);
Utilities.WriteBytesLittleEndian(PartitionEntryCount, buffer, offset + 80);
Utilities.WriteBytesLittleEndian(PartitionEntrySize, buffer, offset + 84);
Utilities.WriteBytesLittleEndian(EntriesCrc, buffer, offset + 88);
// Calculate & write the CRC
Utilities.WriteBytesLittleEndian(CalcCrc(buffer, offset, HeaderSize), buffer, offset + 16);
// Update the cached copy - re-allocate the buffer to allow for HeaderSize potentially having changed
Buffer = new byte[HeaderSize];
Array.Copy(buffer, offset, Buffer, 0, HeaderSize);
}
internal static uint CalcCrc(byte[] buffer, int offset, int count)
{
byte[] temp = new byte[count];
Array.Copy(buffer, offset, temp, 0, count);
// Reset CRC field
Utilities.WriteBytesLittleEndian((uint)0, temp, 16);
return Crc32LittleEndian.Compute(Crc32Algorithm.Common, temp, 0, count);
}
}
}
+120
View File
@@ -0,0 +1,120 @@
//
// 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.
//
namespace DiscUtils.Partitions
{
using System;
using System.IO;
/// <summary>
/// Provides access to partition records in a GUID partition table.
/// </summary>
public sealed class GuidPartitionInfo : PartitionInfo
{
private GuidPartitionTable _table;
private GptEntry _entry;
internal GuidPartitionInfo(GuidPartitionTable table, GptEntry entry)
{
_table = table;
_entry = entry;
}
/// <summary>
/// Gets the first sector of the partion (relative to start of disk) as a Logical Block Address.
/// </summary>
public override long FirstSector
{
get { return _entry.FirstUsedLogicalBlock; }
}
/// <summary>
/// Gets the last sector of the partion (relative to start of disk) as a Logical Block Address (inclusive).
/// </summary>
public override long LastSector
{
get { return _entry.LastUsedLogicalBlock; }
}
/// <summary>
/// Gets the type of the partition, as a GUID.
/// </summary>
public override Guid GuidType
{
get { return _entry.PartitionType; }
}
/// <summary>
/// Always returns Zero.
/// </summary>
public override byte BiosType
{
get { return 0; }
}
/// <summary>
/// Gets the type of the partition as a string.
/// </summary>
public override string TypeAsString
{
get { return _entry.FriendlyPartitionType; }
}
/// <summary>
/// Gets the name of the partition.
/// </summary>
public string Name
{
get { return _entry.Name; }
}
/// <summary>
/// Gets the attributes of the partition.
/// </summary>
public long Attributes
{
get { return (long)_entry.Attributes; }
}
/// <summary>
/// Gets the unique identity of this specific partition.
/// </summary>
public Guid Identity
{
get { return _entry.Identity; }
}
internal override PhysicalVolumeType VolumeType
{
get { return PhysicalVolumeType.GptPartition; }
}
/// <summary>
/// Opens a stream to access the content of the partition.
/// </summary>
/// <returns>The new stream.</returns>
public override SparseStream Open()
{
return _table.Open(_entry);
}
}
}
+651
View File
@@ -0,0 +1,651 @@
//
// 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.
//
namespace DiscUtils.Partitions
{
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Globalization;
using System.IO;
/// <summary>
/// Represents a GUID Partition Table.
/// </summary>
public sealed class GuidPartitionTable : PartitionTable
{
private Stream _diskData;
private Geometry _diskGeometry;
private GptHeader _primaryHeader;
private GptHeader _secondaryHeader;
private byte[] _entryBuffer;
/// <summary>
/// Initializes a new instance of the GuidPartitionTable class.
/// </summary>
/// <param name="disk">The disk containing the partition table.</param>
public GuidPartitionTable(VirtualDisk disk)
{
Init(disk.Content, disk.Geometry);
}
/// <summary>
/// Initializes a new instance of the GuidPartitionTable class.
/// </summary>
/// <param name="disk">The stream containing the disk data.</param>
/// <param name="diskGeometry">The geometry of the disk.</param>
public GuidPartitionTable(Stream disk, Geometry diskGeometry)
{
Init(disk, diskGeometry);
}
/// <summary>
/// Gets the first sector of the disk available to hold partitions.
/// </summary>
public long FirstUsableSector
{
get { return _primaryHeader.FirstUsable; }
}
/// <summary>
/// Gets the last sector of the disk available to hold partitions.
/// </summary>
public long LastUsableSector
{
get { return _primaryHeader.LastUsable; }
}
/// <summary>
/// Gets the unique GPT identifier for this disk.
/// </summary>
public override Guid DiskGuid
{
get { return _primaryHeader.DiskGuid; }
}
/// <summary>
/// Gets a collection of the partitions for storing Operating System file-systems.
/// </summary>
public override ReadOnlyCollection<PartitionInfo> Partitions
{
get
{
return new ReadOnlyCollection<PartitionInfo>(Utilities.Map<GptEntry, GuidPartitionInfo>(GetAllEntries(), (e) => new GuidPartitionInfo(this, e)));
}
}
/// <summary>
/// Creates a new partition table on a disk.
/// </summary>
/// <param name="disk">The disk to initialize.</param>
/// <returns>An object to access the newly created partition table.</returns>
public static GuidPartitionTable Initialize(VirtualDisk disk)
{
return Initialize(disk.Content, disk.Geometry);
}
/// <summary>
/// Creates a new partition table on a disk.
/// </summary>
/// <param name="disk">The stream containing the disk data.</param>
/// <param name="diskGeometry">The geometry of the disk.</param>
/// <returns>An object to access the newly created partition table.</returns>
public static GuidPartitionTable Initialize(Stream disk, Geometry diskGeometry)
{
// Create the protective MBR partition record.
BiosPartitionTable pt = BiosPartitionTable.Initialize(disk, diskGeometry);
pt.CreatePrimaryByCylinder(0, diskGeometry.Cylinders - 1, BiosPartitionTypes.GptProtective, false);
// Create the GPT headers, and blank-out the entry areas
const int EntryCount = 128;
const int EntrySize = 128;
int entrySectors = ((EntryCount * EntrySize) + diskGeometry.BytesPerSector - 1) / diskGeometry.BytesPerSector;
byte[] entriesBuffer = new byte[EntryCount * EntrySize];
// Prepare primary header
GptHeader header = new GptHeader(diskGeometry.BytesPerSector);
header.HeaderLba = 1;
header.AlternateHeaderLba = (disk.Length / diskGeometry.BytesPerSector) - 1;
header.FirstUsable = header.HeaderLba + entrySectors + 1;
header.LastUsable = header.AlternateHeaderLba - entrySectors - 1;
header.DiskGuid = Guid.NewGuid();
header.PartitionEntriesLba = 2;
header.PartitionEntryCount = EntryCount;
header.PartitionEntrySize = EntrySize;
header.EntriesCrc = CalcEntriesCrc(entriesBuffer);
// Write the primary header
byte[] headerBuffer = new byte[diskGeometry.BytesPerSector];
header.WriteTo(headerBuffer, 0);
disk.Position = header.HeaderLba * diskGeometry.BytesPerSector;
disk.Write(headerBuffer, 0, headerBuffer.Length);
// Calc alternate header
header.HeaderLba = header.AlternateHeaderLba;
header.AlternateHeaderLba = 1;
header.PartitionEntriesLba = header.HeaderLba - entrySectors;
// Write the alternate header
header.WriteTo(headerBuffer, 0);
disk.Position = header.HeaderLba * diskGeometry.BytesPerSector;
disk.Write(headerBuffer, 0, headerBuffer.Length);
return new GuidPartitionTable(disk, diskGeometry);
}
/// <summary>
/// Creates a new partition table on a disk containing a single partition.
/// </summary>
/// <param name="disk">The disk to initialize.</param>
/// <param name="type">The partition type for the single partition.</param>
/// <returns>An object to access the newly created partition table.</returns>
public static GuidPartitionTable Initialize(VirtualDisk disk, WellKnownPartitionType type)
{
GuidPartitionTable pt = Initialize(disk);
pt.Create(type, true);
return pt;
}
/// <summary>
/// Creates a new partition that encompasses the entire disk.
/// </summary>
/// <param name="type">The partition type.</param>
/// <param name="active">Whether the partition is active (bootable).</param>
/// <returns>The index of the partition.</returns>
/// <remarks>The partition table must be empty before this method is called,
/// otherwise IOException is thrown.</remarks>
public override int Create(WellKnownPartitionType type, bool active)
{
List<GptEntry> allEntries = new List<GptEntry>(GetAllEntries());
EstablishReservedPartition(allEntries);
// Fill the rest of the disk with the requested partition
long start = FirstAvailableSector(allEntries);
long end = FindLastFreeSector(start, allEntries);
return Create(start, end, GuidPartitionTypes.Convert(type), 0, "Data Partition");
}
/// <summary>
/// Creates a new primary partition with a target size.
/// </summary>
/// <param name="size">The target size (in bytes).</param>
/// <param name="type">The partition type.</param>
/// <param name="active">Whether the partition is active (bootable).</param>
/// <returns>The index of the new partition.</returns>
public override int Create(long size, WellKnownPartitionType type, bool active)
{
if (size < _diskGeometry.BytesPerSector)
{
throw new ArgumentOutOfRangeException("size", size, "size must be at least one sector");
}
long sectorLength = size / _diskGeometry.BytesPerSector;
long start = FindGap(size / _diskGeometry.BytesPerSector, 1);
return Create(start, start + sectorLength - 1, GuidPartitionTypes.Convert(type), 0, "Data Partition");
}
/// <summary>
/// Creates a new aligned partition that encompasses the entire disk.
/// </summary>
/// <param name="type">The partition type.</param>
/// <param name="active">Whether the partition is active (bootable).</param>
/// <param name="alignment">The alignment (in bytes).</param>
/// <returns>The index of the partition.</returns>
/// <remarks>The partition table must be empty before this method is called,
/// otherwise IOException is thrown.</remarks>
/// <remarks>
/// Traditionally partitions were aligned to the physical structure of the underlying disk,
/// however with modern storage greater efficiency is acheived by aligning partitions on
/// large values that are a power of two.
/// </remarks>
public override int CreateAligned(WellKnownPartitionType type, bool active, int alignment)
{
if (alignment % _diskGeometry.BytesPerSector != 0)
{
throw new ArgumentException("Alignment is not a multiple of the sector size");
}
List<GptEntry> allEntries = new List<GptEntry>(GetAllEntries());
EstablishReservedPartition(allEntries);
// Fill the rest of the disk with the requested partition
long start = Utilities.RoundUp(FirstAvailableSector(allEntries), alignment / _diskGeometry.BytesPerSector);
long end = Utilities.RoundDown(FindLastFreeSector(start, allEntries) + 1, alignment / _diskGeometry.BytesPerSector);
if (end <= start)
{
throw new IOException("No available space");
}
return Create(start, end - 1, GuidPartitionTypes.Convert(type), 0, "Data Partition");
}
/// <summary>
/// Creates a new aligned partition with a target size.
/// </summary>
/// <param name="size">The target size (in bytes).</param>
/// <param name="type">The partition type.</param>
/// <param name="active">Whether the partition is active (bootable).</param>
/// <param name="alignment">The alignment (in bytes).</param>
/// <returns>The index of the new partition.</returns>
/// <remarks>
/// Traditionally partitions were aligned to the physical structure of the underlying disk,
/// however with modern storage greater efficiency is achieved by aligning partitions on
/// large values that are a power of two.
/// </remarks>
public override int CreateAligned(long size, WellKnownPartitionType type, bool active, int alignment)
{
if (size < _diskGeometry.BytesPerSector)
{
throw new ArgumentOutOfRangeException("size", size, "size must be at least one sector");
}
if (alignment % _diskGeometry.BytesPerSector != 0)
{
throw new ArgumentException("Alignment is not a multiple of the sector size");
}
if (size % alignment != 0)
{
throw new ArgumentException("Size is not a multiple of the alignment");
}
long sectorLength = size / _diskGeometry.BytesPerSector;
long start = FindGap(size / _diskGeometry.BytesPerSector, alignment / _diskGeometry.BytesPerSector);
return Create(start, start + sectorLength - 1, GuidPartitionTypes.Convert(type), 0, "Data Partition");
}
/// <summary>
/// Creates a new GUID partition on the disk.
/// </summary>
/// <param name="startSector">The first sector of the partition.</param>
/// <param name="endSector">The last sector of the partition.</param>
/// <param name="type">The partition type.</param>
/// <param name="attributes">The partition attributes.</param>
/// <param name="name">The name of the partition.</param>
/// <returns>The index of the new partition.</returns>
/// <remarks>No checking is performed on the parameters, the caller is
/// responsible for ensuring that the partition does not overlap other partitions.</remarks>
public int Create(long startSector, long endSector, Guid type, long attributes, string name)
{
GptEntry newEntry = CreateEntry(startSector, endSector, type, attributes, name);
return GetEntryIndex(newEntry.Identity);
}
/// <summary>
/// Deletes a partition at a given index.
/// </summary>
/// <param name="index">The index of the partition.</param>
public override void Delete(int index)
{
int offset = GetPartitionOffset(index);
Array.Clear(_entryBuffer, offset, _primaryHeader.PartitionEntrySize);
Write();
}
internal SparseStream Open(GptEntry entry)
{
long start = entry.FirstUsedLogicalBlock * _diskGeometry.BytesPerSector;
long end = (entry.LastUsedLogicalBlock + 1) * _diskGeometry.BytesPerSector;
return new SubStream(_diskData, start, end - start);
}
private static uint CalcEntriesCrc(byte[] buffer)
{
return Crc32LittleEndian.Compute(Crc32Algorithm.Common, buffer, 0, buffer.Length);
}
private static int CountEntries<T>(ICollection<T> values, Func<T, bool> pred)
{
int count = 0;
foreach (var val in values)
{
if (pred(val))
{
++count;
}
}
return count;
}
private void Init(Stream disk, Geometry diskGeometry)
{
BiosPartitionTable bpt;
try
{
bpt = new BiosPartitionTable(disk, diskGeometry);
}
catch (IOException ioe)
{
throw new IOException("Invalid GPT disk, protective MBR table not present or invalid", ioe);
}
if (bpt.Count != 1 || bpt[0].BiosType != BiosPartitionTypes.GptProtective)
{
throw new IOException("Invalid GPT disk, protective MBR table is not valid");
}
_diskData = disk;
_diskGeometry = diskGeometry;
disk.Position = diskGeometry.BytesPerSector;
byte[] sector = Utilities.ReadFully(disk, diskGeometry.BytesPerSector);
_primaryHeader = new GptHeader(diskGeometry.BytesPerSector);
if (!_primaryHeader.ReadFrom(sector, 0) || !ReadEntries(_primaryHeader))
{
disk.Position = disk.Length - diskGeometry.BytesPerSector;
disk.Read(sector, 0, sector.Length);
_secondaryHeader = new GptHeader(diskGeometry.BytesPerSector);
if (!_secondaryHeader.ReadFrom(sector, 0) || !ReadEntries(_secondaryHeader))
{
throw new IOException("No valid GUID Partition Table found");
}
// Generate from the primary table from the secondary one
_primaryHeader = new GptHeader(_secondaryHeader);
_primaryHeader.HeaderLba = _secondaryHeader.AlternateHeaderLba;
_primaryHeader.AlternateHeaderLba = _secondaryHeader.HeaderLba;
_primaryHeader.PartitionEntriesLba = 2;
// If the disk is writeable, fix up the primary partition table based on the
// (valid) secondary table.
if (disk.CanWrite)
{
WritePrimaryHeader();
}
}
if (_secondaryHeader == null)
{
_secondaryHeader = new GptHeader(diskGeometry.BytesPerSector);
disk.Position = disk.Length - diskGeometry.BytesPerSector;
disk.Read(sector, 0, sector.Length);
if (!_secondaryHeader.ReadFrom(sector, 0) || !ReadEntries(_secondaryHeader))
{
// Generate from the secondary table from the primary one
_secondaryHeader = new GptHeader(_primaryHeader);
_secondaryHeader.HeaderLba = _secondaryHeader.AlternateHeaderLba;
_secondaryHeader.AlternateHeaderLba = _secondaryHeader.HeaderLba;
_secondaryHeader.PartitionEntriesLba = _secondaryHeader.HeaderLba - Utilities.RoundUp(_secondaryHeader.PartitionEntryCount * _secondaryHeader.PartitionEntrySize, diskGeometry.BytesPerSector);
// If the disk is writeable, fix up the secondary partition table based on the
// (valid) primary table.
if (disk.CanWrite)
{
WriteSecondaryHeader();
}
}
}
}
private void EstablishReservedPartition(List<GptEntry> allEntries)
{
// If no MicrosoftReserved partition, and no Microsoft Data partitions, and the disk
// has a 'reasonable' size free, create a Microsoft Reserved partition.
if (CountEntries(allEntries, e => e.PartitionType == GuidPartitionTypes.MicrosoftReserved) == 0
&& CountEntries(allEntries, e => e.PartitionType == GuidPartitionTypes.WindowsBasicData) == 0
&& _diskGeometry.Capacity > 512 * 1024 * 1024)
{
long reservedStart = FirstAvailableSector(allEntries);
long reservedEnd = FindLastFreeSector(reservedStart, allEntries);
if ((reservedEnd - reservedStart + 1) * _diskGeometry.BytesPerSector > 512 * 1024 * 1024)
{
long size = ((_diskGeometry.Capacity < (16 * 1024L * 1024 * 1024)) ? 32 : 128) * 1024 * 1024;
reservedEnd = reservedStart + (size / _diskGeometry.BytesPerSector) - 1;
int reservedOffset = GetFreeEntryOffset();
GptEntry newReservedEntry = new GptEntry();
newReservedEntry.PartitionType = GuidPartitionTypes.MicrosoftReserved;
newReservedEntry.Identity = Guid.NewGuid();
newReservedEntry.FirstUsedLogicalBlock = reservedStart;
newReservedEntry.LastUsedLogicalBlock = reservedEnd;
newReservedEntry.Attributes = 0;
newReservedEntry.Name = "Microsoft reserved partition";
newReservedEntry.WriteTo(_entryBuffer, reservedOffset);
allEntries.Add(newReservedEntry);
}
}
}
private GptEntry CreateEntry(long startSector, long endSector, Guid type, long attributes, string name)
{
if (endSector < startSector)
{
throw new ArgumentException("The end sector is before the start sector");
}
int offset = GetFreeEntryOffset();
GptEntry newEntry = new GptEntry();
newEntry.PartitionType = type;
newEntry.Identity = Guid.NewGuid();
newEntry.FirstUsedLogicalBlock = startSector;
newEntry.LastUsedLogicalBlock = endSector;
newEntry.Attributes = (ulong)attributes;
newEntry.Name = name;
newEntry.WriteTo(_entryBuffer, offset);
// Commit changes to disk
Write();
return newEntry;
}
private long FindGap(long numSectors, long alignmentSectors)
{
List<GptEntry> list = new List<GptEntry>(GetAllEntries());
list.Sort();
long startSector = Utilities.RoundUp(_primaryHeader.FirstUsable, alignmentSectors);
foreach (var entry in list)
{
if (!Utilities.RangesOverlap(startSector, startSector + numSectors - 1, entry.FirstUsedLogicalBlock, entry.LastUsedLogicalBlock))
{
break;
}
else
{
startSector = Utilities.RoundUp(entry.LastUsedLogicalBlock + 1, alignmentSectors);
}
}
if (_diskGeometry.TotalSectorsLong - startSector < numSectors)
{
throw new IOException(string.Format(CultureInfo.InvariantCulture, "Unable to find free space of {0} sectors", numSectors));
}
return startSector;
}
private long FirstAvailableSector(List<GptEntry> allEntries)
{
long start = _primaryHeader.FirstUsable;
foreach (GptEntry entry in allEntries)
{
if (entry.LastUsedLogicalBlock >= start)
{
start = entry.LastUsedLogicalBlock + 1;
}
}
return start;
}
private long FindLastFreeSector(long start, List<GptEntry> allEntries)
{
long end = _primaryHeader.LastUsable;
foreach (GptEntry entry in allEntries)
{
if (entry.LastUsedLogicalBlock > start && entry.FirstUsedLogicalBlock <= end)
{
end = entry.FirstUsedLogicalBlock - 1;
}
}
return end;
}
private void Write()
{
WritePrimaryHeader();
WriteSecondaryHeader();
}
private void WritePrimaryHeader()
{
byte[] buffer = new byte[_diskGeometry.BytesPerSector];
_primaryHeader.EntriesCrc = CalcEntriesCrc();
_primaryHeader.WriteTo(buffer, 0);
_diskData.Position = _diskGeometry.BytesPerSector;
_diskData.Write(buffer, 0, buffer.Length);
_diskData.Position = 2 * _diskGeometry.BytesPerSector;
_diskData.Write(_entryBuffer, 0, _entryBuffer.Length);
}
private void WriteSecondaryHeader()
{
byte[] buffer = new byte[_diskGeometry.BytesPerSector];
_secondaryHeader.EntriesCrc = CalcEntriesCrc();
_secondaryHeader.WriteTo(buffer, 0);
_diskData.Position = _diskData.Length - _diskGeometry.BytesPerSector;
_diskData.Write(buffer, 0, buffer.Length);
_diskData.Position = _secondaryHeader.PartitionEntriesLba * _diskGeometry.BytesPerSector;
_diskData.Write(_entryBuffer, 0, _entryBuffer.Length);
}
private bool ReadEntries(GptHeader header)
{
_diskData.Position = header.PartitionEntriesLba * _diskGeometry.BytesPerSector;
_entryBuffer = Utilities.ReadFully(_diskData, (int)(header.PartitionEntrySize * header.PartitionEntryCount));
if (header.EntriesCrc != CalcEntriesCrc())
{
return false;
}
return true;
}
private uint CalcEntriesCrc()
{
return Crc32LittleEndian.Compute(Crc32Algorithm.Common, _entryBuffer, 0, _entryBuffer.Length);
}
private IEnumerable<GptEntry> GetAllEntries()
{
for (int i = 0; i < _primaryHeader.PartitionEntryCount; ++i)
{
GptEntry entry = new GptEntry();
entry.ReadFrom(_entryBuffer, i * _primaryHeader.PartitionEntrySize);
if (entry.PartitionType != Guid.Empty)
{
yield return entry;
}
}
}
private int GetPartitionOffset(int index)
{
bool found = false;
int entriesSoFar = 0;
int position = 0;
while (!found && position < _primaryHeader.PartitionEntryCount)
{
GptEntry entry = new GptEntry();
entry.ReadFrom(_entryBuffer, position * _primaryHeader.PartitionEntrySize);
if (entry.PartitionType != Guid.Empty)
{
if (index == entriesSoFar)
{
found = true;
break;
}
entriesSoFar++;
}
position++;
}
if (found)
{
return position * _primaryHeader.PartitionEntrySize;
}
else
{
throw new IOException(string.Format(CultureInfo.InvariantCulture, "No such partition: {0}", index));
}
}
private int GetEntryIndex(Guid identity)
{
int index = 0;
for (int i = 0; i < _primaryHeader.PartitionEntryCount; ++i)
{
GptEntry entry = new GptEntry();
entry.ReadFrom(_entryBuffer, i * _primaryHeader.PartitionEntrySize);
if (entry.Identity == identity)
{
return index;
}
else if (entry.PartitionType != Guid.Empty)
{
index++;
}
}
throw new IOException("No such partition");
}
private int GetFreeEntryOffset()
{
for (int i = 0; i < _primaryHeader.PartitionEntryCount; ++i)
{
GptEntry entry = new GptEntry();
entry.ReadFrom(_entryBuffer, i * _primaryHeader.PartitionEntrySize);
if (entry.PartitionType == Guid.Empty)
{
return i * _primaryHeader.PartitionEntrySize;
}
}
throw new IOException("No free partition entries available");
}
}
}
@@ -0,0 +1,94 @@
//
// 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.
//
namespace DiscUtils.Partitions
{
using System;
/// <summary>
/// Convenient access to well known GPT partition types.
/// </summary>
public static class GuidPartitionTypes
{
/// <summary>
/// EFI system partition.
/// </summary>
public static readonly Guid EfiSystem = new Guid("C12A7328-F81F-11D2-BA4B-00A0C93EC93B");
/// <summary>
/// BIOS boot partition.
/// </summary>
public static readonly Guid BiosBoot = new Guid("21686148-6449-6E6F-744E-656564454649");
/// <summary>
/// Microsoft reserved partition.
/// </summary>
public static readonly Guid MicrosoftReserved = new Guid("E3C9E316-0B5C-4DB8-817D-F92DF00215AE");
/// <summary>
/// Windows basic data partition.
/// </summary>
public static readonly Guid WindowsBasicData = new Guid("EBD0A0A2-B9E5-4433-87C0-68B6B72699C7");
/// <summary>
/// Linux LVM partition.
/// </summary>
public static readonly Guid LinuxLvm = new Guid("E6D6D379-F507-44C2-A23C-238F2A3DF928");
/// <summary>
/// Linux swap partition.
/// </summary>
public static readonly Guid LinuxSwap = new Guid("0657FD6D-A4AB-43C4-84E5-0933C84B4F4F");
/// <summary>
/// Windows Logical Disk Manager metadata.
/// </summary>
public static readonly Guid WindowsLdmMetadata = new Guid("5808C8AA-7E8F-42E0-85D2-E1E90434CFB3");
/// <summary>
/// Windows Logical Disk Manager data.
/// </summary>
public static readonly Guid WindowsLdmData = new Guid("AF9B60A0-1431-4F62-BC68-3311714A69AD");
/// <summary>
/// Converts a well known partition type to a Guid.
/// </summary>
/// <param name="wellKnown">The value to convert.</param>
/// <returns>The GUID value.</returns>
internal static Guid Convert(WellKnownPartitionType wellKnown)
{
switch (wellKnown)
{
case WellKnownPartitionType.Linux:
case WellKnownPartitionType.WindowsFat:
case WellKnownPartitionType.WindowsNtfs:
return WindowsBasicData;
case WellKnownPartitionType.LinuxLvm:
return LinuxLvm;
case WellKnownPartitionType.LinuxSwap:
return LinuxSwap;
default:
throw new ArgumentException("Unknown partition type");
}
}
}
}
+95
View File
@@ -0,0 +1,95 @@
//
// 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.
//
namespace DiscUtils.Partitions
{
using System;
using System.Globalization;
using System.IO;
/// <summary>
/// Base class representing a disk partition.
/// </summary>
/// <remarks>The purpose of this class is to provide a minimal view of a partition,
/// such that callers can access existing partitions without specific knowledge of
/// the partitioning system.</remarks>
public abstract class PartitionInfo
{
/// <summary>
/// Gets the first sector of the partion (relative to start of disk) as a Logical Block Address.
/// </summary>
public abstract long FirstSector { get; }
/// <summary>
/// Gets the last sector of the partion (relative to start of disk) as a Logical Block Address (inclusive).
/// </summary>
public abstract long LastSector { get; }
/// <summary>
/// Gets the length of the partition in sectors.
/// </summary>
public virtual long SectorCount
{
get { return 1 + LastSector - FirstSector; }
}
/// <summary>
/// Gets the type of the partition, as a GUID, when available.
/// </summary>
/// <remarks><see cref="System.Guid"/>.Empty for MBR-style partitions.</remarks>
public abstract Guid GuidType { get; }
/// <summary>
/// Gets the type of the partition, in legacy BIOS form, when available.
/// </summary>
/// <remarks>Zero for GUID-style partitions.</remarks>
public abstract byte BiosType { get; }
/// <summary>
/// Gets the partition type as a 'friendly' string.
/// </summary>
public abstract string TypeAsString { get; }
/// <summary>
/// Gets the physical volume type for this type of partition.
/// </summary>
internal abstract PhysicalVolumeType VolumeType
{
get;
}
/// <summary>
/// Opens a stream that accesses the partition's contents.
/// </summary>
/// <returns>The new stream.</returns>
public abstract SparseStream Open();
/// <summary>
/// Gets a summary of the partition information as 'first - last (type)'.
/// </summary>
/// <returns>A string representation of the partition information.</returns>
public override string ToString()
{
return String.Format(CultureInfo.InvariantCulture, "0x{0:X} - 0x{1:X} ({2})", FirstSector, LastSector, TypeAsString);
}
}
}
+208
View File
@@ -0,0 +1,208 @@
//
// 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.
//
namespace DiscUtils.Partitions
{
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
/// <summary>
/// Base class for classes which represent a disk partitioning scheme.
/// </summary>
/// <remarks>After modifying the table, by creating or deleting a partition assume that any
/// previously stored partition indexes of higher value are no longer valid. Re-enumerate
/// the partitions to discover the next index-to-partition mapping.</remarks>
public abstract class PartitionTable
{
private static List<PartitionTableFactory> s_factories;
/// <summary>
/// Gets the GUID that uniquely identifies this disk, if supported (else returns <c>null</c>).
/// </summary>
public abstract Guid DiskGuid { get; }
/// <summary>
/// Gets the list of partitions that contain user data (i.e. non-system / empty).
/// </summary>
public abstract ReadOnlyCollection<PartitionInfo> Partitions { get; }
/// <summary>
/// Gets the number of User partitions on the disk.
/// </summary>
public int Count
{
get { return Partitions.Count; }
}
private static List<PartitionTableFactory> Factories
{
get
{
if (s_factories == null)
{
List<PartitionTableFactory> factories = new List<PartitionTableFactory>();
foreach (var type in typeof(VolumeManager).Assembly.GetTypes())
{
foreach (PartitionTableFactoryAttribute attr in Attribute.GetCustomAttributes(type, typeof(PartitionTableFactoryAttribute), false))
{
factories.Add((PartitionTableFactory)Activator.CreateInstance(type));
}
}
s_factories = factories;
}
return s_factories;
}
}
/// <summary>
/// Gets information about a particular User partition.
/// </summary>
/// <param name="index">The index of the partition.</param>
/// <returns>Information about the partition.</returns>
public PartitionInfo this[int index]
{
get { return Partitions[index]; }
}
/// <summary>
/// Determines if a disk is partitioned with a known partitioning scheme.
/// </summary>
/// <param name="content">The content of the disk to check.</param>
/// <returns><c>true</c> if the disk is partitioned, else <c>false</c>.</returns>
public static bool IsPartitioned(Stream content)
{
foreach (var partTableFactory in Factories)
{
if (partTableFactory.DetectIsPartitioned(content))
{
return true;
}
}
return false;
}
/// <summary>
/// Determines if a disk is partitioned with a known partitioning scheme.
/// </summary>
/// <param name="disk">The disk to check.</param>
/// <returns><c>true</c> if the disk is partitioned, else <c>false</c>.</returns>
public static bool IsPartitioned(VirtualDisk disk)
{
return IsPartitioned(disk.Content);
}
/// <summary>
/// Gets all of the partition tables found on a disk.
/// </summary>
/// <param name="disk">The disk to inspect.</param>
/// <returns>It is rare for a disk to have multiple partition tables, but theoretically
/// possible.</returns>
public static IList<PartitionTable> GetPartitionTables(VirtualDisk disk)
{
List<PartitionTable> tables = new List<PartitionTable>();
foreach (var factory in Factories)
{
PartitionTable table = factory.DetectPartitionTable(disk);
if (table != null)
{
tables.Add(table);
}
}
return tables;
}
/// <summary>
/// Gets all of the partition tables found on a disk.
/// </summary>
/// <param name="contentStream">The content of the disk to inspect.</param>
/// <returns>It is rare for a disk to have multiple partition tables, but theoretically
/// possible.</returns>
public static IList<PartitionTable> GetPartitionTables(Stream contentStream)
{
return GetPartitionTables(new Raw.Disk(contentStream, Ownership.None));
}
/// <summary>
/// Creates a new partition that encompasses the entire disk.
/// </summary>
/// <param name="type">The partition type.</param>
/// <param name="active">Whether the partition is active (bootable).</param>
/// <returns>The index of the partition.</returns>
/// <remarks>The partition table must be empty before this method is called,
/// otherwise IOException is thrown.</remarks>
public abstract int Create(WellKnownPartitionType type, bool active);
/// <summary>
/// Creates a new partition with a target size.
/// </summary>
/// <param name="size">The target size (in bytes).</param>
/// <param name="type">The partition type.</param>
/// <param name="active">Whether the partition is active (bootable).</param>
/// <returns>The index of the new partition.</returns>
public abstract int Create(long size, WellKnownPartitionType type, bool active);
/// <summary>
/// Creates a new aligned partition that encompasses the entire disk.
/// </summary>
/// <param name="type">The partition type.</param>
/// <param name="active">Whether the partition is active (bootable).</param>
/// <param name="alignment">The alignment (in byte).</param>
/// <returns>The index of the partition.</returns>
/// <remarks>The partition table must be empty before this method is called,
/// otherwise IOException is thrown.</remarks>
/// <remarks>
/// Traditionally partitions were aligned to the physical structure of the underlying disk,
/// however with modern storage greater efficiency is acheived by aligning partitions on
/// large values that are a power of two.
/// </remarks>
public abstract int CreateAligned(WellKnownPartitionType type, bool active, int alignment);
/// <summary>
/// Creates a new aligned partition with a target size.
/// </summary>
/// <param name="size">The target size (in bytes).</param>
/// <param name="type">The partition type.</param>
/// <param name="active">Whether the partition is active (bootable).</param>
/// <param name="alignment">The alignment (in byte).</param>
/// <returns>The index of the new partition.</returns>
/// <remarks>
/// Traditionally partitions were aligned to the physical structure of the underlying disk,
/// however with modern storage greater efficiency is achieved by aligning partitions on
/// large values that are a power of two.
/// </remarks>
public abstract int CreateAligned(long size, WellKnownPartitionType type, bool active, int alignment);
/// <summary>
/// Deletes a partition at a given index.
/// </summary>
/// <param name="index">The index of the partition.</param>
public abstract void Delete(int index);
}
}
@@ -0,0 +1,33 @@
//
// 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.
//
namespace DiscUtils.Partitions
{
using System.IO;
internal abstract class PartitionTableFactory
{
public abstract bool DetectIsPartitioned(Stream s);
public abstract PartitionTable DetectPartitionTable(VirtualDisk disk);
}
}
@@ -0,0 +1,31 @@
//
// 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.
//
namespace DiscUtils.Partitions
{
using System;
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
internal sealed class PartitionTableFactoryAttribute : Attribute
{
}
}
@@ -0,0 +1,55 @@
//
// 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.
//
namespace DiscUtils.Partitions
{
/// <summary>
/// Enumeration of partition-table technology neutral partition types.
/// </summary>
public enum WellKnownPartitionType
{
/// <summary>
/// Windows FAT-based partition.
/// </summary>
WindowsFat = 0,
/// <summary>
/// Windows NTFS-based partition.
/// </summary>
WindowsNtfs = 1,
/// <summary>
/// Linux native file system.
/// </summary>
Linux = 2,
/// <summary>
/// Linux swap.
/// </summary>
LinuxSwap = 3,
/// <summary>
/// Linux Logical Volume Manager (LVM).
/// </summary>
LinuxLvm = 4,
}
}