public void Create() { ChsAddress g = new ChsAddress(100, 16, 63); Assert.Equal(100, g.Cylinder); Assert.Equal(16, g.Head); Assert.Equal(63, g.Sector); }
/// <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) * Utilities.SectorSize), active)); }
/// <summary> /// Determines if this object is equivalent to another. /// </summary> /// <param name="obj">The object to test against.</param> /// <returns><c>true</c> if the <paramref name="obj"/> is equivalent, else <c>false</c>.</returns> public override bool Equals(object obj) { if (obj == null || obj.GetType() != GetType()) { return(false); } ChsAddress other = (ChsAddress)obj; return(_cylinder == other._cylinder && _head == other._head && _sector == other._sector); }
/// <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)); }
public void LBARoundTrip() { Geometry g = new Geometry(100, 16, 63); const int TestCylinder = 54; const int TestHead = 15; const int TestSector = 63; long lba = g.ToLogicalBlockAddress(TestCylinder, TestHead, TestSector); ChsAddress chs = g.ToChsAddress(lba); Assert.Equal(TestCylinder, chs.Cylinder); Assert.Equal(TestHead, chs.Head); Assert.Equal(TestSector, chs.Sector); }
/// <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; }
/// <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> /// 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> /// 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) * Utilities.SectorSize), active); }
/// <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); }