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);
        }
Example #2
0
        /// <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));
        }
Example #3
0
        /// <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);
        }
Example #4
0
        /// <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));
        }
Example #5
0
        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);
        }
Example #6
0
        /// <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;
        }
Example #7
0
        /// <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>
        /// 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 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);
        }