// /*************************************************************************** // Aaru Data Preservation Suite // ---------------------------------------------------------------------------- // // Filename : MBR.cs // Author(s) : Natalia Portillo // // Component : Partitioning scheme plugins. // // --[ Description ] ---------------------------------------------------------- // // Manages Intel/Microsoft MBR (aka Master Boot Record). // // --[ License ] -------------------------------------------------------------- // // This library is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as // published by the Free Software Foundation; either version 2.1 of the // License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, see . // // ---------------------------------------------------------------------------- // Copyright © 2011-2025 Natalia Portillo // ****************************************************************************/ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; using Aaru.CommonTypes; using Aaru.CommonTypes.Enums; using Aaru.CommonTypes.Interfaces; using Aaru.Helpers; using Aaru.Logging; using Marshal = Aaru.Helpers.Marshal; namespace Aaru.Partitions; // TODO: Support AAP extensions /// /// Implements decoding of Intel/Microsoft Master Boot Record and extensions public sealed class MBR : IPartition { const ulong GPT_MAGIC = 0x5452415020494645; const ushort MBR_MAGIC = 0xAA55; const ushort NEC_MAGIC = 0xA55A; const ushort DM_MAGIC = 0x55AA; const string MODULE_NAME = "Master Boot Record (MBR) plugin"; [SuppressMessage("ReSharper", "StringLiteralTypo")] static readonly string[] _mbrTypes = [ // 0x00 Localization.Empty, Localization.FAT12, Localization.XENIX_root, Localization.XENIX_usr, // 0x04 Localization.FAT16_32_MiB, Localization.Extended, Localization.FAT16, Localization.IFS_HPFS_NTFS, // 0x08 Localization.AIX_boot_OS2_Commodore_DOS, Localization.AIX_data_Coherent_QNX, Localization.Coherent_swap_OPUS_OS_2_Boot_Manager, Localization.FAT32, // 0x0C Localization.FAT32_LBA, Localization.Unknown_partition_type, Localization.FAT16_LBA, Localization.Extended_LBA, // 0x10 Localization.OPUS, Localization.Hidden_FAT12, Localization.Compaq_diagnostics_recovery_partition, Localization.Unknown_partition_type, // 0x14 Localization.Hidden_FAT16_32_MiB_AST_DOS, Localization.Unknown_partition_type, Localization.Hidden_FAT16, Localization.Hidden_IFS_HPFS_NTFS, // 0x18 Localization.AST_Windows_swap, Localization.Willowtech_Photon_coS, Localization.Unknown_partition_type, Localization.Hidden_FAT32, // 0x1C Localization.Hidden_FAT32_LBA, Localization.Unknown_partition_type, Localization.Hidden_FAT16_LBA, Localization.Unknown_partition_type, // 0x20 Localization.Willowsoft_Overture_File_System, Localization.Oxygen_FSo2, Localization.Oxygen_Extended, Localization.SpeedStor_reserved, // 0x24 Localization.NEC_DOS, Localization.Unknown_partition_type, Localization.SpeedStor_reserved, Localization.Hidden_NTFS, // 0x28 Localization.Unknown_partition_type, Localization.Unknown_partition_type, Localization.Unknown_partition_type, Localization.Unknown_partition_type, // 0x2C Localization.Unknown_partition_type, Localization.Unknown_partition_type, Localization.Unknown_partition_type, Localization.Unknown_partition_type, // 0x30 Localization.Unknown_partition_type, Localization.SpeedStor_reserved, Localization.Unknown_partition_type, Localization.SpeedStor_reserved, // 0x34 Localization.SpeedStor_reserved, Localization.Unknown_partition_type, Localization.SpeedStor_reserved, Localization.Unknown_partition_type, // 0x38 Localization.Theos, Localization.Plan_9, Localization.Unknown_partition_type, Localization.Unknown_partition_type, // 0x3C Localization.Partition_Magic, Localization.Hidden_NetWare, Localization.Unknown_partition_type, Localization.Unknown_partition_type, // 0x40 Localization.VENIX_80286, Localization.PReP_Boot, Localization.Secure_File_System, Localization.PTS_DOS, // 0x44 Localization.Unknown_partition_type, Localization.Priam_EUMEL_Elan, Localization.EUMEL_Elan, Localization.EUMEL_Elan, // 0x48 Localization.EUMEL_Elan, Localization.Unknown_partition_type, Localization.ALFS_THIN_lightweight_filesystem_for_DOS, Localization.Unknown_partition_type, // 0x4C Localization.Unknown_partition_type, Localization.QNX_4, Localization.QNX_4, Localization.QNX_4_Oberon, // 0x50 Localization.Ontrack_DM_RO_FAT, Localization.Ontrack_DM_RW_FAT, Localization.CPM_Microport_UNIX, Localization.Ontrack_DM_6, // 0x54 Localization.Ontrack_DM_6, Localization.EZ_Drive, Localization.Golden_Bow_VFeature, Localization.Unknown_partition_type, // 0x58 Localization.Unknown_partition_type, Localization.Unknown_partition_type, Localization.Unknown_partition_type, Localization.Unknown_partition_type, // 0x5C Localization.Priam_EDISK, Localization.Unknown_partition_type, Localization.Unknown_partition_type, Localization.Unknown_partition_type, // 0x60 Localization.Unknown_partition_type, Localization.SpeedStor, Localization.Unknown_partition_type, Localization.GNU_Hurd_System_V_386ix, // 0x64 Localization.NetWare_286, Localization.NetWare, Localization.NetWare_386, Localization.NetWare, // 0x68 Localization.NetWare, Localization.NetWare_NSS, Localization.Unknown_partition_type, Localization.Unknown_partition_type, // 0x6C Localization.Unknown_partition_type, Localization.Unknown_partition_type, Localization.Unknown_partition_type, Localization.Unknown_partition_type, // 0x70 Localization.DiskSecure_Multi_Boot, Localization.Unknown_partition_type, Localization.UNIX_7th_Edition, Localization.Unknown_partition_type, // 0x74 Localization.Unknown_partition_type, Localization.IBM_PC_IX, Localization.Unknown_partition_type, Localization.Unknown_partition_type, // 0x78 Localization.Unknown_partition_type, Localization.Unknown_partition_type, Localization.Unknown_partition_type, Localization.Unknown_partition_type, // 0x7C Localization.Unknown_partition_type, Localization.Unknown_partition_type, Localization.Unknown_partition_type, Localization.Unknown_partition_type, // 0x80 Localization.Old_MINIX, Localization.MINIX_Old_Linux, Localization.Linux_swap_Solaris, Localization.Linux, // 0x84 Localization.Hidden_by_OS2_APM_hibernation, Localization.Linux_extended, Localization.NT_Stripe_Set, Localization.NT_Stripe_Set, // 0x88 Localization.Linux_Plaintext, Localization.Unknown_partition_type, Localization.Unknown_partition_type, Localization.Unknown_partition_type, // 0x8C Localization.Unknown_partition_type, Localization.Unknown_partition_type, Localization.Linux_LVM, Localization.Unknown_partition_type, // 0x90 Localization.Unknown_partition_type, Localization.Unknown_partition_type, Localization.Unknown_partition_type, Localization.Amoeba_Hidden_Linux, // 0x94 Localization.Amoeba_bad_blocks, Localization.Unknown_partition_type, Localization.Unknown_partition_type, Localization.Unknown_partition_type, // 0x98 Localization.Unknown_partition_type, Localization.Mylex_EISA_SCSI, Localization.Unknown_partition_type, Localization.Unknown_partition_type, // 0x9C Localization.Unknown_partition_type, Localization.Unknown_partition_type, Localization.Unknown_partition_type, Localization.BSD_OS, // 0xA0 Localization.Hibernation, Localization.HP_Volume_Expansion, Localization.Unknown_partition_type, Localization.HP_Volume_Expansion, // 0xA4 Localization.HP_Volume_Expansion, Localization.FreeBSD, Localization.OpenBSD, Localization.NeXTStep, // 0xA8 Localization.Apple_UFS, Localization.NetBSD, Localization.Olivetti_DOS_FAT12, Localization.Apple_Boot, // 0xAC Localization.Unknown_partition_type, Localization.Unknown_partition_type, Localization.Unknown_partition_type, Localization.Apple_HFS, // 0xB0 Localization.BootStar, Localization.HP_Volume_Expansion, Localization.Unknown_partition_type, Localization.HP_Volume_Expansion, // 0xB4 Localization.HP_Volume_Expansion, Localization.Unknown_partition_type, Localization.HP_Volume_Expansion, Localization.BSDi, // 0xB8 "BSDi swap", Localization.Unknown_partition_type, Localization.Unknown_partition_type, Localization.PTS_BootWizard, // 0xBC Localization.Unknown_partition_type, Localization.Unknown_partition_type, Localization.Solaris_boot, Localization.Solaris, // 0xC0 Localization.Novell_DOS_DR_DOS_secured, Localization.DR_DOS_secured_FAT12, Localization.DR_DOS_reserved, Localization.DR_DOS_reserved, // 0xC4 Localization.DR_DOS_secured_FAT16_32_MiB, Localization.Unknown_partition_type, Localization.DR_DOS_secured_FAT16, Localization.Syrinx, // 0xC8 Localization.DR_DOS_reserved, Localization.DR_DOS_reserved, Localization.DR_DOS_reserved, Localization.DR_DOS_secured_FAT32, // 0xCC Localization.DR_DOS_secured_FAT32_LBA, Localization.DR_DOS_reserved, Localization.DR_DOS_secured_FAT16_LBA, Localization.DR_DOS_secured_extended_LBA, // 0xD0 Localization.Multiuser_DOS_secured_FAT12, Localization.Multiuser_DOS_secured_FAT12, Localization.Unknown_partition_type, Localization.Unknown_partition_type, // 0xD4 Localization.Multiuser_DOS_secured_FAT16_32_MiB, Localization.Multiuser_DOS_secured_extended, Localization.Multiuser_DOS_secured_FAT16, Localization.Unknown_partition_type, // 0xD8 Localization.CPM, Localization.Unknown_partition_type, Localization.Filesystem_less_data, Localization.CPM_CCPM_CTOS, // 0xDC Localization.Unknown_partition_type, Localization.Unknown_partition_type, Localization.Dell_partition, Localization.BootIt_EMBRM, // 0xE0 Localization.Unknown_partition_type, Localization.SpeedStor, Localization.DOS_read_only, Localization.SpeedStor, // 0xE4 Localization.SpeedStor, Localization.Tandy_DOS, Localization.SpeedStor, Localization.Unknown_partition_type, // 0xE8 Localization.Unknown_partition_type, Localization.Unknown_partition_type, Localization.Unknown_partition_type, Localization.BeOS, // 0xEC Localization.Unknown_partition_type, Localization.Sprytx, Localization.Guid_Partition_Table, Localization.EFI_system_partition, // 0xF0 Localization.Linux_boot, Localization.SpeedStor, Localization.DOS_3_3_secondary_Unisys_DOS, Localization.SpeedStor, // 0xF4 Localization.SpeedStor, Localization.Prologue, Localization.SpeedStor, Localization.Unknown_partition_type, // 0xF8 Localization.Unknown_partition_type, Localization.Unknown_partition_type, Localization.Unknown_partition_type, Localization.VMware_VMFS, // 0xFC Localization.VMWare_VMKCORE, Localization.Linux_RAID_FreeDOS, Localization.SpeedStor_LANStep_PS2_IML, Localization.Xenix_bad_block ]; static bool GetMinix(IMediaImage imagePlugin, ulong start, ulong divider, ulong sectorOffset, uint sectorSize, out List partitions) { partitions = []; ErrorNumber errno = imagePlugin.ReadSector(start, false, out byte[] sector, out _); if(errno != ErrorNumber.NoError) return false; ExtendedBootRecord mnx = Marshal.ByteArrayToStructureLittleEndian(sector); AaruLogging.Debug(MODULE_NAME, "mnx.magic == MBR_Magic = {0}", mnx.magic == MBR_MAGIC); if(mnx.magic != MBR_MAGIC) return false; var anyMnx = false; foreach(PartitionEntry mnxEntry in mnx.entries) { var mnxValid = true; var startSector = (byte)(mnxEntry.start_sector & 0x3F); var startCylinder = (ushort)((mnxEntry.start_sector & 0xC0) << 2 | mnxEntry.start_cylinder); var endSector = (byte)(mnxEntry.end_sector & 0x3F); var endCylinder = (ushort)((mnxEntry.end_sector & 0xC0) << 2 | mnxEntry.end_cylinder); ulong mnxStart = mnxEntry.lba_start; ulong mnxSectors = mnxEntry.lba_sectors; AaruLogging.Debug(MODULE_NAME, "mnx_entry.status {0}", mnxEntry.status); AaruLogging.Debug(MODULE_NAME, "mnx_entry.type {0}", mnxEntry.type); AaruLogging.Debug(MODULE_NAME, "mnx_entry.lba_start {0}", mnxEntry.lba_start); AaruLogging.Debug(MODULE_NAME, "mnx_entry.lba_sectors {0}", mnxEntry.lba_sectors); AaruLogging.Debug(MODULE_NAME, "mnx_entry.start_cylinder {0}", startCylinder); AaruLogging.Debug(MODULE_NAME, "mnx_entry.start_head {0}", mnxEntry.start_head); AaruLogging.Debug(MODULE_NAME, "mnx_entry.start_sector {0}", startSector); AaruLogging.Debug(MODULE_NAME, "mnx_entry.end_cylinder {0}", endCylinder); AaruLogging.Debug(MODULE_NAME, "mnx_entry.end_head {0}", mnxEntry.end_head); AaruLogging.Debug(MODULE_NAME, "mnx_entry.end_sector {0}", endSector); mnxValid &= mnxEntry.status is 0x00 or 0x80; mnxValid &= mnxEntry.type is 0x81 or 0x80; mnxValid &= mnxEntry.lba_start != 0 || mnxEntry.lba_sectors != 0 || mnxEntry.start_cylinder != 0 || mnxEntry.start_head != 0 || mnxEntry.start_sector != 0 || mnxEntry.end_cylinder != 0 || mnxEntry.end_head != 0 || mnxEntry.end_sector != 0; if(mnxEntry is { lba_start: 0, lba_sectors: 0 } && mnxValid) { mnxStart = CHS.ToLBA(startCylinder, mnxEntry.start_head, startSector, imagePlugin.Info.Heads, imagePlugin.Info.SectorsPerTrack); mnxSectors = CHS.ToLBA(endCylinder, mnxEntry.end_head, mnxEntry.end_sector, imagePlugin.Info.Heads, imagePlugin.Info.SectorsPerTrack) - mnxStart; } // For optical media mnxStart /= divider; mnxSectors /= divider; AaruLogging.Debug(MODULE_NAME, "mnx_start {0}", mnxStart); AaruLogging.Debug(MODULE_NAME, "mnx_sectors {0}", mnxSectors); if(!mnxValid) continue; var part = new Partition(); if(mnxStart > 0 && mnxSectors > 0) { part.Start = mnxStart + sectorOffset; part.Length = mnxSectors; part.Offset = part.Start * sectorSize; part.Size = part.Length * sectorSize; } else mnxValid = false; if(!mnxValid) continue; anyMnx = true; part.Type = "MINIX"; part.Name = Localization.MINIX; part.Description = mnxEntry.status == 0x80 ? Localization.Partition_is_bootable : ""; part.Scheme = "MINIX"; partitions.Add(part); } return anyMnx; } static string DecodeMbrType(byte type) => _mbrTypes[type]; #region Nested type: DiskManagerMasterBootRecord [StructLayout(LayoutKind.Sequential, Pack = 1)] readonly struct DiskManagerMasterBootRecord { /// Boot code [MarshalAs(UnmanagedType.ByValArray, SizeConst = 252)] public readonly byte[] boot_code; /// /// /// public readonly ushort dm_magic; /// Partitions [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] public readonly PartitionEntry[] entries; /// /// /// public readonly ushort magic; } #endregion #region Nested type: ExtendedBootRecord // TODO: IBM Boot Manager [StructLayout(LayoutKind.Sequential, Pack = 1)] readonly struct ExtendedBootRecord { /// Boot code, almost always unused [MarshalAs(UnmanagedType.ByValArray, SizeConst = 446)] public readonly byte[] boot_code; /// Partitions or pointers [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] public readonly PartitionEntry[] entries; /// /// /// public readonly ushort magic; } #endregion #region Nested type: MasterBootRecord [StructLayout(LayoutKind.Sequential, Pack = 1)] readonly struct MasterBootRecord { /// Boot code [MarshalAs(UnmanagedType.ByValArray, SizeConst = 446)] public readonly byte[] boot_code; /// Partitions [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] public readonly PartitionEntry[] entries; /// /// /// public readonly ushort magic; } #endregion #region Nested type: ModernMasterBootRecord [StructLayout(LayoutKind.Sequential, Pack = 1)] readonly struct ModernMasterBootRecord { /// Boot code [MarshalAs(UnmanagedType.ByValArray, SizeConst = 218)] public readonly byte[] boot_code; /// Set to 0 public readonly ushort zero; /// Original physical drive public readonly byte drive; /// Disk timestamp, seconds public readonly byte seconds; /// Disk timestamp, minutes public readonly byte minutes; /// Disk timestamp, hours public readonly byte hours; /// Boot code, continuation [MarshalAs(UnmanagedType.ByValArray, SizeConst = 216)] public readonly byte[] boot_code2; /// Disk serial number public readonly uint serial; /// Set to 0 public readonly ushort zero2; /// Partitions [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] public readonly PartitionEntry[] entries; /// /// /// public readonly ushort magic; } #endregion #region Nested type: NecMasterBootRecord [StructLayout(LayoutKind.Sequential, Pack = 1)] readonly struct NecMasterBootRecord { /// Boot code [MarshalAs(UnmanagedType.ByValArray, SizeConst = 380)] public readonly byte[] boot_code; /// /// /// public readonly ushort nec_magic; /// Partitions [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] public readonly PartitionEntry[] entries; /// /// /// public readonly ushort magic; } #endregion #region Nested type: PartitionEntry [StructLayout(LayoutKind.Sequential, Pack = 1)] readonly struct PartitionEntry { /// Partition status, 0x80 or 0x00, else invalid public readonly byte status; /// Starting head [0,254] public readonly byte start_head; /// Starting sector [1,63] public readonly byte start_sector; /// Starting cylinder [0,1023] public readonly byte start_cylinder; /// Partition type public readonly byte type; /// Ending head [0,254] public readonly byte end_head; /// Ending sector [1,63] public readonly byte end_sector; /// Ending cylinder [0,1023] public readonly byte end_cylinder; /// Starting absolute sector public readonly uint lba_start; /// Total sectors public readonly uint lba_sectors; } #endregion #region Nested type: SerializedMasterBootRecord [StructLayout(LayoutKind.Sequential, Pack = 1)] readonly struct SerializedMasterBootRecord { /// Boot code [MarshalAs(UnmanagedType.ByValArray, SizeConst = 440)] public readonly byte[] boot_code; /// Disk serial number public readonly uint serial; /// Set to 0 public readonly ushort zero; /// Partitions [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] public readonly PartitionEntry[] entries; /// /// /// public readonly ushort magic; } #endregion #region Nested type: TimedMasterBootRecord [StructLayout(LayoutKind.Sequential, Pack = 1)] readonly struct TimedMasterBootRecord { /// Boot code [MarshalAs(UnmanagedType.ByValArray, SizeConst = 218)] public readonly byte[] boot_code; /// Set to 0 public readonly ushort zero; /// Original physical drive public readonly byte drive; /// Disk timestamp, seconds public readonly byte seconds; /// Disk timestamp, minutes public readonly byte minutes; /// Disk timestamp, hours public readonly byte hours; /// Boot code, continuation [MarshalAs(UnmanagedType.ByValArray, SizeConst = 222)] public readonly byte[] boot_code2; /// Partitions [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] public readonly PartitionEntry[] entries; /// /// /// public readonly ushort magic; } #endregion #region IPartition Members /// public string Name => Localization.MBR_Name; /// public Guid Id => new("5E8A34E8-4F1A-59E6-4BF7-7EA647063A76"); /// public string Author => Authors.NATALIA_PORTILLO; /// [SuppressMessage("ReSharper", "UnusedVariable")] public bool GetInformation(IMediaImage imagePlugin, out List partitions, ulong sectorOffset) { ulong counter = 0; partitions = []; if(imagePlugin.Info.SectorSize < 512) return false; uint sectorSize = imagePlugin.Info.SectorSize; // Divider of sector size in MBR between real sector size ulong divider = 1; if(imagePlugin.Info.MetadataMediaType == MetadataMediaType.OpticalDisc) { sectorSize = 512; divider = 4; } ErrorNumber errno = imagePlugin.ReadSector(sectorOffset, false, out byte[] sector, out _); if(errno != ErrorNumber.NoError) return false; MasterBootRecord mbr = Marshal.ByteArrayToStructureLittleEndian(sector); TimedMasterBootRecord mbrTime = Marshal.ByteArrayToStructureLittleEndian(sector); SerializedMasterBootRecord mbrSerial = Marshal.ByteArrayToStructureLittleEndian(sector); ModernMasterBootRecord mbrModern = Marshal.ByteArrayToStructureLittleEndian(sector); NecMasterBootRecord mbrNec = Marshal.ByteArrayToStructureLittleEndian(sector); DiskManagerMasterBootRecord mbrOntrack = Marshal.ByteArrayToStructureLittleEndian(sector); AaruLogging.Debug(MODULE_NAME, "xmlmedia = {0}", imagePlugin.Info.MetadataMediaType); AaruLogging.Debug(MODULE_NAME, "mbr.magic = {0:X4}", mbr.magic); if(mbr.magic != MBR_MAGIC) return false; // Not MBR errno = imagePlugin.ReadSector(1 + sectorOffset, false, out byte[] hdrBytes, out _); if(errno != ErrorNumber.NoError) return false; var signature = BitConverter.ToUInt64(hdrBytes, 0); AaruLogging.Debug(MODULE_NAME, "gpt.signature = 0x{0:X16}", signature); if(signature == GPT_MAGIC) return false; if(imagePlugin.Info.MetadataMediaType == MetadataMediaType.OpticalDisc) { errno = imagePlugin.ReadSector(sectorOffset, false, out hdrBytes, out _); if(errno != ErrorNumber.NoError) return false; signature = BitConverter.ToUInt64(hdrBytes, 512); AaruLogging.Debug(MODULE_NAME, "gpt.signature @ 0x200 = 0x{0:X16}", signature); if(signature == GPT_MAGIC) return false; } PartitionEntry[] entries; if(mbrOntrack.dm_magic == DM_MAGIC) entries = mbrOntrack.entries; else if(mbrNec.nec_magic == NEC_MAGIC) entries = mbrNec.entries; else entries = mbr.entries; foreach(PartitionEntry entry in entries) { var startSector = (byte)(entry.start_sector & 0x3F); var startCylinder = (ushort)((entry.start_sector & 0xC0) << 2 | entry.start_cylinder); var endSector = (byte)(entry.end_sector & 0x3F); var endCylinder = (ushort)((entry.end_sector & 0xC0) << 2 | entry.end_cylinder); ulong lbaStart = entry.lba_start; ulong lbaSectors = entry.lba_sectors; // Let's start the fun... var valid = true; var extended = false; var minix = false; if(entry.status != 0x00 && entry.status != 0x80) return false; // Maybe a FAT filesystem valid &= entry.type != 0x00; if(entry.type is 0x05 or 0x0F or 0x15 or 0x1F or 0x85 or 0x91 or 0x9B or 0xC5 or 0xCF or 0xD5) { valid = false; extended = true; // Extended partition } minix |= entry.type is 0x81 or 0x80; // MINIX partition valid &= entry.lba_start != 0 || entry.lba_sectors != 0 || entry.start_cylinder != 0 || entry.start_head != 0 || entry.start_sector != 0 || entry.end_cylinder != 0 || entry.end_head != 0 || entry.end_sector != 0; if(entry is { lba_start: 0, lba_sectors: 0 } && valid) { lbaStart = CHS.ToLBA(startCylinder, entry.start_head, startSector, imagePlugin.Info.Heads, imagePlugin.Info.SectorsPerTrack); lbaSectors = CHS.ToLBA(endCylinder, entry.end_head, entry.end_sector, imagePlugin.Info.Heads, imagePlugin.Info.SectorsPerTrack) - lbaStart; } // For optical media lbaStart /= divider; lbaSectors /= divider; if(minix && lbaStart == sectorOffset) minix = false; if(lbaStart > imagePlugin.Info.Sectors) { valid = false; extended = false; } // Some buggy implementations do some rounding errors getting a few sectors beyond device size if(lbaStart + lbaSectors > imagePlugin.Info.Sectors) lbaSectors = imagePlugin.Info.Sectors - lbaStart; AaruLogging.Debug(MODULE_NAME, "entry.status {0}", entry.status); AaruLogging.Debug(MODULE_NAME, "entry.type {0}", entry.type); AaruLogging.Debug(MODULE_NAME, "entry.lba_start {0}", entry.lba_start); AaruLogging.Debug(MODULE_NAME, "entry.lba_sectors {0}", entry.lba_sectors); AaruLogging.Debug(MODULE_NAME, "entry.start_cylinder {0}", startCylinder); AaruLogging.Debug(MODULE_NAME, "entry.start_head {0}", entry.start_head); AaruLogging.Debug(MODULE_NAME, "entry.start_sector {0}", startSector); AaruLogging.Debug(MODULE_NAME, "entry.end_cylinder {0}", endCylinder); AaruLogging.Debug(MODULE_NAME, "entry.end_head {0}", entry.end_head); AaruLogging.Debug(MODULE_NAME, "entry.end_sector {0}", endSector); AaruLogging.Debug(MODULE_NAME, "entry.minix = {0}", minix); AaruLogging.Debug(MODULE_NAME, "lba_start {0}", lbaStart); AaruLogging.Debug(MODULE_NAME, "lba_sectors {0}", lbaSectors); if(valid && minix) // Let's mix the fun { if(GetMinix(imagePlugin, lbaStart, divider, sectorOffset, sectorSize, out List mnxParts)) partitions.AddRange(mnxParts); else minix = false; } if(valid && !minix) { var part = new Partition(); if((lbaStart > 0 || imagePlugin.Info.MetadataMediaType == MetadataMediaType.OpticalDisc) && lbaSectors > 0) { part.Start = lbaStart + sectorOffset; part.Length = lbaSectors; part.Offset = part.Start * sectorSize; part.Size = part.Length * sectorSize; } else valid = false; if(valid) { part.Type = $"0x{entry.type:X2}"; part.Name = DecodeMbrType(entry.type); part.Sequence = counter; part.Description = entry.status == 0x80 ? Localization.Partition_is_bootable : ""; part.Scheme = Name; counter++; partitions.Add(part); } } AaruLogging.Debug(MODULE_NAME, "entry.extended = {0}", extended); if(!extended) continue; var processingExtended = true; ulong chainStart = lbaStart; while(processingExtended) { errno = imagePlugin.ReadSector(lbaStart, false, out sector, out _); if(errno != ErrorNumber.NoError) break; ExtendedBootRecord ebr = Marshal.ByteArrayToStructureLittleEndian(sector); AaruLogging.Debug(MODULE_NAME, "ebr.magic == MBR_Magic = {0}", ebr.magic == MBR_MAGIC); if(ebr.magic != MBR_MAGIC) break; ulong nextStart = 0; foreach(PartitionEntry ebrEntry in ebr.entries) { var extValid = true; startSector = (byte)(ebrEntry.start_sector & 0x3F); startCylinder = (ushort)((ebrEntry.start_sector & 0xC0) << 2 | ebrEntry.start_cylinder); endSector = (byte)(ebrEntry.end_sector & 0x3F); endCylinder = (ushort)((ebrEntry.end_sector & 0xC0) << 2 | ebrEntry.end_cylinder); ulong extStart = ebrEntry.lba_start; ulong extSectors = ebrEntry.lba_sectors; var extMinix = false; AaruLogging.Debug(MODULE_NAME, "ebr_entry.status {0}", ebrEntry.status); AaruLogging.Debug(MODULE_NAME, "ebr_entry.type {0}", ebrEntry.type); AaruLogging.Debug(MODULE_NAME, "ebr_entry.lba_start {0}", ebrEntry.lba_start); AaruLogging.Debug(MODULE_NAME, "ebr_entry.lba_sectors {0}", ebrEntry.lba_sectors); AaruLogging.Debug(MODULE_NAME, "ebr_entry.start_cylinder {0}", startCylinder); AaruLogging.Debug(MODULE_NAME, "ebr_entry.start_head {0}", ebrEntry.start_head); AaruLogging.Debug(MODULE_NAME, "ebr_entry.start_sector {0}", startSector); AaruLogging.Debug(MODULE_NAME, "ebr_entry.end_cylinder {0}", endCylinder); AaruLogging.Debug(MODULE_NAME, "ebr_entry.end_head {0}", ebrEntry.end_head); AaruLogging.Debug(MODULE_NAME, "ebr_entry.end_sector {0}", endSector); // Let's start the fun... extValid &= ebrEntry.status is 0x00 or 0x80; extValid &= ebrEntry.type != 0x00; extValid &= ebrEntry.lba_start != 0 || ebrEntry.lba_sectors != 0 || ebrEntry.start_cylinder != 0 || ebrEntry.start_head != 0 || ebrEntry.start_sector != 0 || ebrEntry.end_cylinder != 0 || ebrEntry.end_head != 0 || ebrEntry.end_sector != 0; if(ebrEntry is { lba_start: 0, lba_sectors: 0 } && extValid) { extStart = CHS.ToLBA(startCylinder, ebrEntry.start_head, startSector, imagePlugin.Info.Heads, imagePlugin.Info.SectorsPerTrack); extSectors = CHS.ToLBA(endCylinder, ebrEntry.end_head, ebrEntry.end_sector, imagePlugin.Info.Heads, imagePlugin.Info.SectorsPerTrack) - extStart; } extMinix |= ebrEntry.type is 0x81 or 0x80; // For optical media extStart /= divider; extSectors /= divider; AaruLogging.Debug(MODULE_NAME, "ext_start {0}", extStart); AaruLogging.Debug(MODULE_NAME, "ext_sectors {0}", extSectors); if(ebrEntry.type is 0x05 or 0x0F or 0x15 or 0x1F or 0x85 or 0x91 or 0x9B or 0xC5 or 0xCF or 0xD5) { extValid = false; nextStart = chainStart + extStart; } extStart += lbaStart; extValid &= extStart <= imagePlugin.Info.Sectors; // Some buggy implementations do some rounding errors getting a few sectors beyond device size if(extStart + extSectors > imagePlugin.Info.Sectors) extSectors = imagePlugin.Info.Sectors - extStart; if(extValid && extMinix) // Let's mix the fun { if(GetMinix(imagePlugin, lbaStart, divider, sectorOffset, sectorSize, out List mnxParts)) partitions.AddRange(mnxParts); else extMinix = false; } if(!extValid || extMinix) continue; var part = new Partition(); if(extStart > 0 && extSectors > 0) { part.Start = extStart + sectorOffset; part.Length = extSectors; part.Offset = part.Start * sectorSize; part.Size = part.Length * sectorSize; } else extValid = false; if(!extValid) continue; part.Type = $"0x{ebrEntry.type:X2}"; part.Name = DecodeMbrType(ebrEntry.type); part.Sequence = counter; part.Description = ebrEntry.status == 0x80 ? Localization.Partition_is_bootable : ""; part.Scheme = Name; counter++; partitions.Add(part); } AaruLogging.Debug(MODULE_NAME, "next_start {0}", nextStart); processingExtended &= nextStart != 0; processingExtended &= nextStart <= imagePlugin.Info.Sectors; lbaStart = nextStart; } } // An empty MBR may exist, NeXT creates one and then hardcodes its disklabel return partitions.Count != 0; } #endregion }