// /*************************************************************************** // The Disc Image Chef // ---------------------------------------------------------------------------- // // 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-2019 Natalia Portillo // ****************************************************************************/ using System; using System.Collections.Generic; using System.Runtime.InteropServices; using DiscImageChef.CommonTypes; using DiscImageChef.CommonTypes.Enums; using DiscImageChef.CommonTypes.Interfaces; using DiscImageChef.Console; using DiscImageChef.Helpers; using Marshal = System.Runtime.InteropServices.Marshal; namespace DiscImageChef.Partitions { // TODO: Support AAP extensions public class MBR : IPartition { const ulong GPT_MAGIC = 0x5452415020494645; const ushort MBR_MAGIC = 0xAA55; const ushort NEC_MAGIC = 0xA55A; const ushort DM_MAGIC = 0x55AA; static readonly string[] MbrTypes = { // 0x00 "Empty", "FAT12", "XENIX root", "XENIX /usr", // 0x04 "FAT16 < 32 MiB", "Extended", "FAT16", "IFS (HPFS/NTFS)", // 0x08 "AIX boot, OS/2, Commodore DOS", "AIX data, Coherent, QNX", "Coherent swap, OPUS, OS/2 Boot Manager", "FAT32", // 0x0C "FAT32 (LBA)", "Unknown", "FAT16 (LBA)", "Extended (LBA)", // 0x10 "OPUS", "Hidden FAT12", "Compaq diagnostics, recovery partition", "Unknown", // 0x14 "Hidden FAT16 < 32 MiB, AST-DOS", "Unknown", "Hidden FAT16", "Hidden IFS (HPFS/NTFS)", // 0x18 "AST-Windows swap", "Willowtech Photon coS", "Unknown", "Hidden FAT32", // 0x1C "Hidden FAT32 (LBA)", "Unknown", "Hidden FAT16 (LBA)", "Unknown", // 0x20 "Willowsoft Overture File System", "Oxygen FSo2", "Oxygen Extended ", "SpeedStor reserved", // 0x24 "NEC-DOS", "Unknown", "SpeedStor reserved", "Hidden NTFS", // 0x28 "Unknown", "Unknown", "Unknown", "Unknown", // 0x2C "Unknown", "Unknown", "Unknown", "Unknown", // 0x30 "Unknown", "SpeedStor reserved", "Unknown", "SpeedStor reserved", // 0x34 "SpeedStor reserved", "Unknown", "SpeedStor reserved", "Unknown", // 0x38 "Theos", "Plan 9", "Unknown", "Unknown", // 0x3C "Partition Magic", "Hidden NetWare", "Unknown", "Unknown", // 0x40 "VENIX 80286", "PReP Boot", "Secure File System", "PTS-DOS", // 0x44 "Unknown", "Priam, EUMEL/Elan", "EUMEL/Elan", "EUMEL/Elan", // 0x48 "EUMEL/Elan", "Unknown", "ALFS/THIN lightweight filesystem for DOS", "Unknown", // 0x4C "Unknown", "QNX 4", "QNX 4", "QNX 4, Oberon", // 0x50 "Ontrack DM, R/O, FAT", "Ontrack DM, R/W, FAT", "CP/M, Microport UNIX", "Ontrack DM 6", // 0x54 "Ontrack DM 6", "EZ-Drive", "Golden Bow VFeature", "Unknown", // 0x58 "Unknown", "Unknown", "Unknown", "Unknown", // 0x5C "Priam EDISK", "Unknown", "Unknown", "Unknown", // 0x60 "Unknown", "SpeedStor", "Unknown", "GNU Hurd, System V, 386/ix", // 0x64 "NetWare 286", "NetWare", "NetWare 386", "NetWare", // 0x68 "NetWare", "NetWare NSS", "Unknown", "Unknown", // 0x6C "Unknown", "Unknown", "Unknown", "Unknown", // 0x70 "DiskSecure Multi-Boot", "Unknown", "UNIX 7th Edition", "Unknown", // 0x74 "Unknown", "IBM PC/IX", "Unknown", "Unknown", // 0x78 "Unknown", "Unknown", "Unknown", "Unknown", // 0x7C "Unknown", "Unknown", "Unknown", "Unknown", // 0x80 "Old MINIX", "MINIX, Old Linux", "Linux swap, Solaris", "Linux", // 0x84 "Hidden by OS/2, APM hibernation", "Linux extended", "NT Stripe Set", "NT Stripe Set", // 0x88 "Linux Plaintext", "Unknown", "Unknown", "Unknown", // 0x8C "Unknown", "Unknown", "Linux LVM", "Unknown", // 0x90 "Unknown", "Unknown", "Unknown", "Amoeba, Hidden Linux", // 0x94 "Amoeba bad blocks", "Unknown", "Unknown", "Unknown", // 0x98 "Unknown", "Mylex EISA SCSI", "Unknown", "Unknown", // 0x9C "Unknown", "Unknown", "Unknown", "BSD/OS", // 0xA0 "Hibernation", "HP Volume Expansion", "Unknown", "HP Volume Expansion", // 0xA4 "HP Volume Expansion", "FreeBSD", "OpenBSD", "NeXTStep", // 0xA8 "Apple UFS", "NetBSD", "Olivetti DOS FAT12", "Apple Boot", // 0xAC "Unknown", "Unknown", "Unknown", "Apple HFS", // 0xB0 "BootStar", "HP Volume Expansion", "Unknown", "HP Volume Expansion", // 0xB4 "HP Volume Expansion", "Unknown", "HP Volume Expansion", "BSDi", // 0xB8 "BSDi swap", "Unknown", "Unknown", "PTS BootWizard", // 0xBC "Unknown", "Unknown", "Solaris boot", "Solaris", // 0xC0 "Novell DOS, DR-DOS secured", "DR-DOS secured FAT12", "DR-DOS reserved", "DR-DOS reserved", // 0xC4 "DR-DOS secured FAT16 < 32 MiB", "Unknown", "DR-DOS secured FAT16", "Syrinx", // 0xC8 "DR-DOS reserved", "DR-DOS reserved", "DR-DOS reserved", "DR-DOS secured FAT32", // 0xCC "DR-DOS secured FAT32 (LBA)", "DR-DOS reserved", "DR-DOS secured FAT16 (LBA)", "DR-DOS secured extended (LBA)", // 0xD0 "Multiuser DOS secured FAT12", "Multiuser DOS secured FAT12", "Unknown", "Unknown", // 0xD4 "Multiuser DOS secured FAT16 < 32 MiB", "Multiuser DOS secured extended", "Multiuser DOS secured FAT16", "Unknown", // 0xD8 "CP/M", "Unknown", "Filesystem-less data", "CP/M, CCP/M, CTOS", // 0xDC "Unknown", "Unknown", "Dell partition", "BootIt EMBRM", // 0xE0 "Unknown", "SpeedStor", "DOS read/only", "SpeedStor", // 0xE4 "SpeedStor", "Tandy DOS", "SpeedStor", "Unknown", // 0xE8 "Unknown", "Unknown", "Unknown", "BeOS", // 0xEC "Unknown", "Spryt*x", "Guid Partition Table", "EFI system partition", // 0xF0 "Linux boot", "SpeedStor", "DOS 3.3 secondary, Unisys DOS", "SpeedStor", // 0xF4 "SpeedStor", "Prologue", "SpeedStor", "Unknown", // 0xF8 "Unknown", "Unknown", "Unknown", "VMWare VMFS", // 0xFC "VMWare VMKCORE", "Linux RAID, FreeDOS", "SpeedStor, LANStep, PS/2 IML", "Xenix bad block" }; public string Name => "Master Boot Record"; public Guid Id => new Guid("5E8A34E8-4F1A-59E6-4BF7-7EA647063A76"); public string Author => "Natalia Portillo"; public bool GetInformation(IMediaImage imagePlugin, out List partitions, ulong sectorOffset) { ulong counter = 0; partitions = new List(); 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.XmlMediaType == XmlMediaType.OpticalDisc) { sectorSize = 512; divider = 4; } byte[] sector = imagePlugin.ReadSector(sectorOffset); GCHandle handle = GCHandle.Alloc(sector, GCHandleType.Pinned); MasterBootRecord mbr = (MasterBootRecord)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(MasterBootRecord)); TimedMasterBootRecord mbrTime = (TimedMasterBootRecord)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(TimedMasterBootRecord)); SerializedMasterBootRecord mbrSerial = (SerializedMasterBootRecord)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(SerializedMasterBootRecord)); ModernMasterBootRecord mbrModern = (ModernMasterBootRecord)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(ModernMasterBootRecord)); NecMasterBootRecord mbrNec = (NecMasterBootRecord)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(NecMasterBootRecord)); DiskManagerMasterBootRecord mbrOntrack = (DiskManagerMasterBootRecord)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(DiskManagerMasterBootRecord)); handle.Free(); DicConsole.DebugWriteLine("MBR plugin", "xmlmedia = {0}", imagePlugin.Info.XmlMediaType); DicConsole.DebugWriteLine("MBR plugin", "mbr.magic = {0:X4}", mbr.magic); if(mbr.magic != MBR_MAGIC) return false; // Not MBR byte[] hdrBytes = imagePlugin.ReadSector(1 + sectorOffset); ulong signature = BitConverter.ToUInt64(hdrBytes, 0); DicConsole.DebugWriteLine("MBR Plugin", "gpt.signature = 0x{0:X16}", signature); if(signature == GPT_MAGIC) return false; if(signature != GPT_MAGIC && imagePlugin.Info.XmlMediaType == XmlMediaType.OpticalDisc) { hdrBytes = imagePlugin.ReadSector(sectorOffset); signature = BitConverter.ToUInt64(hdrBytes, 512); DicConsole.DebugWriteLine("MBR Plugin", "gpt.signature @ 0x200 = 0x{0:X16}", signature); if(signature == GPT_MAGIC) return false; } MbrPartitionEntry[] 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(MbrPartitionEntry entry in entries) { byte startSector = (byte)(entry.start_sector & 0x3F); ushort startCylinder = (ushort)(((entry.start_sector & 0xC0) << 2) | entry.start_cylinder); byte endSector = (byte)(entry.end_sector & 0x3F); ushort 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... bool valid = true; bool extended = false; bool minix = false; if(entry.status != 0x00 && entry.status != 0x80) return false; // Maybe a FAT filesystem valid &= entry.type != 0x00; if(entry.type == 0x05 || entry.type == 0x0F || entry.type == 0x15 || entry.type == 0x1F || entry.type == 0x85 || entry.type == 0x91 || entry.type == 0x9B || entry.type == 0xC5 || entry.type == 0xCF || entry.type == 0xD5) { valid = false; extended = true; // Extended partition } minix |= entry.type == 0x81 || entry.type == 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.lba_start == 0 && entry.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; DicConsole.DebugWriteLine("MBR plugin", "entry.status {0}", entry.status); DicConsole.DebugWriteLine("MBR plugin", "entry.type {0}", entry.type); DicConsole.DebugWriteLine("MBR plugin", "entry.lba_start {0}", entry.lba_start); DicConsole.DebugWriteLine("MBR plugin", "entry.lba_sectors {0}", entry.lba_sectors); DicConsole.DebugWriteLine("MBR plugin", "entry.start_cylinder {0}", startCylinder); DicConsole.DebugWriteLine("MBR plugin", "entry.start_head {0}", entry.start_head); DicConsole.DebugWriteLine("MBR plugin", "entry.start_sector {0}", startSector); DicConsole.DebugWriteLine("MBR plugin", "entry.end_cylinder {0}", endCylinder); DicConsole.DebugWriteLine("MBR plugin", "entry.end_head {0}", entry.end_head); DicConsole.DebugWriteLine("MBR plugin", "entry.end_sector {0}", endSector); DicConsole.DebugWriteLine("MBR plugin", "entry.minix = {0}", minix); DicConsole.DebugWriteLine("MBR plugin", "lba_start {0}", lbaStart); DicConsole.DebugWriteLine("MBR plugin", "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) { Partition part = new Partition(); if((lbaStart > 0 || imagePlugin.Info.XmlMediaType == XmlMediaType.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 ? "Partition is bootable." : ""; part.Scheme = Name; counter++; partitions.Add(part); } } DicConsole.DebugWriteLine("MBR plugin", "entry.extended = {0}", extended); if(!extended) continue; bool processingExtended = true; ulong chainStart = lbaStart; while(processingExtended) { sector = imagePlugin.ReadSector(lbaStart); handle = GCHandle.Alloc(sector, GCHandleType.Pinned); ExtendedBootRecord ebr = (ExtendedBootRecord)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(ExtendedBootRecord)); handle.Free(); DicConsole.DebugWriteLine("MBR plugin", "ebr.magic == MBR_Magic = {0}", ebr.magic == MBR_MAGIC); if(ebr.magic != MBR_MAGIC) break; ulong nextStart = 0; foreach(MbrPartitionEntry ebrEntry in ebr.entries) { bool 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; bool extMinix = false; DicConsole.DebugWriteLine("MBR plugin", "ebr_entry.status {0}", ebrEntry.status); DicConsole.DebugWriteLine("MBR plugin", "ebr_entry.type {0}", ebrEntry.type); DicConsole.DebugWriteLine("MBR plugin", "ebr_entry.lba_start {0}", ebrEntry.lba_start); DicConsole.DebugWriteLine("MBR plugin", "ebr_entry.lba_sectors {0}", ebrEntry.lba_sectors); DicConsole.DebugWriteLine("MBR plugin", "ebr_entry.start_cylinder {0}", startCylinder); DicConsole.DebugWriteLine("MBR plugin", "ebr_entry.start_head {0}", ebrEntry.start_head); DicConsole.DebugWriteLine("MBR plugin", "ebr_entry.start_sector {0}", startSector); DicConsole.DebugWriteLine("MBR plugin", "ebr_entry.end_cylinder {0}", endCylinder); DicConsole.DebugWriteLine("MBR plugin", "ebr_entry.end_head {0}", ebrEntry.end_head); DicConsole.DebugWriteLine("MBR plugin", "ebr_entry.end_sector {0}", endSector); // Let's start the fun... extValid &= ebrEntry.status == 0x00 || ebrEntry.status == 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.lba_start == 0 && ebrEntry.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 == 0x81 || ebrEntry.type == 0x80; // For optical media extStart /= divider; extSectors /= divider; DicConsole.DebugWriteLine("MBR plugin", "ext_start {0}", extStart); DicConsole.DebugWriteLine("MBR plugin", "ext_sectors {0}", extSectors); if(ebrEntry.type == 0x05 || ebrEntry.type == 0x0F || ebrEntry.type == 0x15 || ebrEntry.type == 0x1F || ebrEntry.type == 0x85 || ebrEntry.type == 0x91 || ebrEntry.type == 0x9B || ebrEntry.type == 0xC5 || ebrEntry.type == 0xCF || ebrEntry.type == 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; Partition 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 ? "Partition is bootable." : ""; part.Scheme = Name; counter++; partitions.Add(part); } DicConsole.DebugWriteLine("MBR plugin", "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; } static bool GetMinix(IMediaImage imagePlugin, ulong start, ulong divider, ulong sectorOffset, uint sectorSize, out List partitions) { partitions = new List(); byte[] sector = imagePlugin.ReadSector(start); GCHandle handle = GCHandle.Alloc(sector, GCHandleType.Pinned); ExtendedBootRecord mnx = (ExtendedBootRecord)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(ExtendedBootRecord)); handle.Free(); DicConsole.DebugWriteLine("MBR plugin", "mnx.magic == MBR_Magic = {0}", mnx.magic == MBR_MAGIC); if(mnx.magic != MBR_MAGIC) return false; bool anyMnx = false; foreach(MbrPartitionEntry mnxEntry in mnx.entries) { bool mnxValid = true; byte startSector = (byte)(mnxEntry.start_sector & 0x3F); ushort startCylinder = (ushort)(((mnxEntry.start_sector & 0xC0) << 2) | mnxEntry.start_cylinder); byte endSector = (byte)(mnxEntry.end_sector & 0x3F); ushort endCylinder = (ushort)(((mnxEntry.end_sector & 0xC0) << 2) | mnxEntry.end_cylinder); ulong mnxStart = mnxEntry.lba_start; ulong mnxSectors = mnxEntry.lba_sectors; DicConsole.DebugWriteLine("MBR plugin", "mnx_entry.status {0}", mnxEntry.status); DicConsole.DebugWriteLine("MBR plugin", "mnx_entry.type {0}", mnxEntry.type); DicConsole.DebugWriteLine("MBR plugin", "mnx_entry.lba_start {0}", mnxEntry.lba_start); DicConsole.DebugWriteLine("MBR plugin", "mnx_entry.lba_sectors {0}", mnxEntry.lba_sectors); DicConsole.DebugWriteLine("MBR plugin", "mnx_entry.start_cylinder {0}", startCylinder); DicConsole.DebugWriteLine("MBR plugin", "mnx_entry.start_head {0}", mnxEntry.start_head); DicConsole.DebugWriteLine("MBR plugin", "mnx_entry.start_sector {0}", startSector); DicConsole.DebugWriteLine("MBR plugin", "mnx_entry.end_cylinder {0}", endCylinder); DicConsole.DebugWriteLine("MBR plugin", "mnx_entry.end_head {0}", mnxEntry.end_head); DicConsole.DebugWriteLine("MBR plugin", "mnx_entry.end_sector {0}", endSector); mnxValid &= mnxEntry.status == 0x00 || mnxEntry.status == 0x80; mnxValid &= mnxEntry.type == 0x81 || mnxEntry.type == 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.lba_start == 0 && mnxEntry.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; DicConsole.DebugWriteLine("MBR plugin", "mnx_start {0}", mnxStart); DicConsole.DebugWriteLine("MBR plugin", "mnx_sectors {0}", mnxSectors); if(!mnxValid) continue; Partition 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 = "MINIX"; part.Description = mnxEntry.status == 0x80 ? "Partition is bootable." : ""; part.Scheme = "MINIX"; partitions.Add(part); } return anyMnx; } static string DecodeMbrType(byte type) => MbrTypes[type]; [StructLayout(LayoutKind.Sequential, Pack = 1)] struct MasterBootRecord { /// Boot code [MarshalAs(UnmanagedType.ByValArray, SizeConst = 446)] public byte[] boot_code; /// Partitions [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] public MbrPartitionEntry[] entries; /// /// /// public ushort magic; } // TODO: IBM Boot Manager [StructLayout(LayoutKind.Sequential, Pack = 1)] struct ExtendedBootRecord { /// Boot code, almost always unused [MarshalAs(UnmanagedType.ByValArray, SizeConst = 446)] public byte[] boot_code; /// Partitions or pointers [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] public MbrPartitionEntry[] entries; /// /// /// public ushort magic; } [StructLayout(LayoutKind.Sequential, Pack = 1)] struct TimedMasterBootRecord { /// Boot code [MarshalAs(UnmanagedType.ByValArray, SizeConst = 218)] public byte[] boot_code; /// Set to 0 public ushort zero; /// Original physical drive public byte drive; /// Disk timestamp, seconds public byte seconds; /// Disk timestamp, minutes public byte minutes; /// Disk timestamp, hours public byte hours; /// Boot code, continuation [MarshalAs(UnmanagedType.ByValArray, SizeConst = 222)] public byte[] boot_code2; /// Partitions [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] public MbrPartitionEntry[] entries; /// /// /// public ushort magic; } [StructLayout(LayoutKind.Sequential, Pack = 1)] struct SerializedMasterBootRecord { /// Boot code [MarshalAs(UnmanagedType.ByValArray, SizeConst = 440)] public byte[] boot_code; /// Disk serial number public uint serial; /// Set to 0 public ushort zero; /// Partitions [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] public MbrPartitionEntry[] entries; /// /// /// public ushort magic; } [StructLayout(LayoutKind.Sequential, Pack = 1)] struct ModernMasterBootRecord { /// Boot code [MarshalAs(UnmanagedType.ByValArray, SizeConst = 218)] public byte[] boot_code; /// Set to 0 public ushort zero; /// Original physical drive public byte drive; /// Disk timestamp, seconds public byte seconds; /// Disk timestamp, minutes public byte minutes; /// Disk timestamp, hours public byte hours; /// Boot code, continuation [MarshalAs(UnmanagedType.ByValArray, SizeConst = 216)] public byte[] boot_code2; /// Disk serial number public uint serial; /// Set to 0 public ushort zero2; /// Partitions [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] public MbrPartitionEntry[] entries; /// /// /// public ushort magic; } [StructLayout(LayoutKind.Sequential, Pack = 1)] struct NecMasterBootRecord { /// Boot code [MarshalAs(UnmanagedType.ByValArray, SizeConst = 380)] public byte[] boot_code; /// /// /// public ushort nec_magic; /// Partitions [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] public MbrPartitionEntry[] entries; /// /// /// public ushort magic; } [StructLayout(LayoutKind.Sequential, Pack = 1)] struct DiskManagerMasterBootRecord { /// Boot code [MarshalAs(UnmanagedType.ByValArray, SizeConst = 252)] public byte[] boot_code; /// /// /// public ushort dm_magic; /// Partitions [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] public MbrPartitionEntry[] entries; /// /// /// public ushort magic; } [StructLayout(LayoutKind.Sequential, Pack = 1)] struct MbrPartitionEntry { /// Partition status, 0x80 or 0x00, else invalid public byte status; /// Starting head [0,254] public byte start_head; /// Starting sector [1,63] public byte start_sector; /// Starting cylinder [0,1023] public byte start_cylinder; /// Partition type public byte type; /// Ending head [0,254] public byte end_head; /// Ending sector [1,63] public byte end_sector; /// Ending cylinder [0,1023] public byte end_cylinder; /// Starting absolute sector public uint lba_start; /// Total sectors public uint lba_sectors; } } }