mirror of
https://github.com/aaru-dps/Aaru.git
synced 2025-12-16 19:24:25 +00:00
945 lines
38 KiB
C#
945 lines
38 KiB
C#
// /***************************************************************************
|
|
// Aaru Data Preservation Suite
|
|
// ----------------------------------------------------------------------------
|
|
//
|
|
// Filename : MBR.cs
|
|
// Author(s) : Natalia Portillo <claunia@claunia.com>
|
|
//
|
|
// 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 <http://www.gnu.org/licenses/>.
|
|
//
|
|
// ----------------------------------------------------------------------------
|
|
// Copyright © 2011-2023 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.Console;
|
|
using Aaru.Helpers;
|
|
using Marshal = Aaru.Helpers.Marshal;
|
|
|
|
namespace Aaru.Partitions;
|
|
|
|
// TODO: Support AAP extensions
|
|
/// <inheritdoc />
|
|
/// <summary>Implements decoding of Intel/Microsoft Master Boot Record and extensions</summary>
|
|
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
|
|
};
|
|
|
|
#region IPartition Members
|
|
|
|
/// <inheritdoc />
|
|
public string Name => Localization.MBR_Name;
|
|
|
|
/// <inheritdoc />
|
|
public Guid Id => new("5E8A34E8-4F1A-59E6-4BF7-7EA647063A76");
|
|
|
|
/// <inheritdoc />
|
|
public string Author => Authors.NataliaPortillo;
|
|
|
|
/// <inheritdoc />
|
|
[SuppressMessage("ReSharper", "UnusedVariable")]
|
|
public bool GetInformation(IMediaImage imagePlugin, out List<Partition> partitions, ulong sectorOffset)
|
|
{
|
|
ulong counter = 0;
|
|
|
|
partitions = new List<Partition>();
|
|
|
|
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, out byte[] sector);
|
|
|
|
if(errno != ErrorNumber.NoError)
|
|
return false;
|
|
|
|
MasterBootRecord mbr = Marshal.ByteArrayToStructureLittleEndian<MasterBootRecord>(sector);
|
|
TimedMasterBootRecord mbrTime = Marshal.ByteArrayToStructureLittleEndian<TimedMasterBootRecord>(sector);
|
|
|
|
SerializedMasterBootRecord mbrSerial =
|
|
Marshal.ByteArrayToStructureLittleEndian<SerializedMasterBootRecord>(sector);
|
|
|
|
ModernMasterBootRecord mbrModern = Marshal.ByteArrayToStructureLittleEndian<ModernMasterBootRecord>(sector);
|
|
NecMasterBootRecord mbrNec = Marshal.ByteArrayToStructureLittleEndian<NecMasterBootRecord>(sector);
|
|
|
|
DiskManagerMasterBootRecord mbrOntrack =
|
|
Marshal.ByteArrayToStructureLittleEndian<DiskManagerMasterBootRecord>(sector);
|
|
|
|
AaruConsole.DebugWriteLine(MODULE_NAME, "xmlmedia = {0}", imagePlugin.Info.MetadataMediaType);
|
|
AaruConsole.DebugWriteLine(MODULE_NAME, "mbr.magic = {0:X4}", mbr.magic);
|
|
|
|
if(mbr.magic != MBR_MAGIC)
|
|
return false; // Not MBR
|
|
|
|
errno = imagePlugin.ReadSector(1 + sectorOffset, out byte[] hdrBytes);
|
|
|
|
if(errno != ErrorNumber.NoError)
|
|
return false;
|
|
|
|
var signature = BitConverter.ToUInt64(hdrBytes, 0);
|
|
|
|
AaruConsole.DebugWriteLine(MODULE_NAME, "gpt.signature = 0x{0:X16}", signature);
|
|
|
|
if(signature == GPT_MAGIC)
|
|
return false;
|
|
|
|
if(imagePlugin.Info.MetadataMediaType == MetadataMediaType.OpticalDisc)
|
|
{
|
|
errno = imagePlugin.ReadSector(sectorOffset, out hdrBytes);
|
|
|
|
if(errno != ErrorNumber.NoError)
|
|
return false;
|
|
|
|
signature = BitConverter.ToUInt64(hdrBytes, 512);
|
|
AaruConsole.DebugWriteLine(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;
|
|
|
|
AaruConsole.DebugWriteLine(MODULE_NAME, "entry.status {0}", entry.status);
|
|
AaruConsole.DebugWriteLine(MODULE_NAME, "entry.type {0}", entry.type);
|
|
AaruConsole.DebugWriteLine(MODULE_NAME, "entry.lba_start {0}", entry.lba_start);
|
|
AaruConsole.DebugWriteLine(MODULE_NAME, "entry.lba_sectors {0}", entry.lba_sectors);
|
|
AaruConsole.DebugWriteLine(MODULE_NAME, "entry.start_cylinder {0}", startCylinder);
|
|
AaruConsole.DebugWriteLine(MODULE_NAME, "entry.start_head {0}", entry.start_head);
|
|
AaruConsole.DebugWriteLine(MODULE_NAME, "entry.start_sector {0}", startSector);
|
|
AaruConsole.DebugWriteLine(MODULE_NAME, "entry.end_cylinder {0}", endCylinder);
|
|
AaruConsole.DebugWriteLine(MODULE_NAME, "entry.end_head {0}", entry.end_head);
|
|
AaruConsole.DebugWriteLine(MODULE_NAME, "entry.end_sector {0}", endSector);
|
|
|
|
AaruConsole.DebugWriteLine(MODULE_NAME, "entry.minix = {0}", minix);
|
|
|
|
AaruConsole.DebugWriteLine(MODULE_NAME, "lba_start {0}", lbaStart);
|
|
AaruConsole.DebugWriteLine(MODULE_NAME, "lba_sectors {0}", lbaSectors);
|
|
|
|
if(valid && minix) // Let's mix the fun
|
|
{
|
|
if(GetMinix(imagePlugin, lbaStart, divider, sectorOffset, sectorSize, out List<Partition> 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);
|
|
}
|
|
}
|
|
|
|
AaruConsole.DebugWriteLine(MODULE_NAME, "entry.extended = {0}", extended);
|
|
|
|
if(!extended)
|
|
continue;
|
|
|
|
var processingExtended = true;
|
|
ulong chainStart = lbaStart;
|
|
|
|
while(processingExtended)
|
|
{
|
|
errno = imagePlugin.ReadSector(lbaStart, out sector);
|
|
|
|
if(errno != ErrorNumber.NoError)
|
|
break;
|
|
|
|
ExtendedBootRecord ebr = Marshal.ByteArrayToStructureLittleEndian<ExtendedBootRecord>(sector);
|
|
|
|
AaruConsole.DebugWriteLine(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;
|
|
|
|
AaruConsole.DebugWriteLine(MODULE_NAME, "ebr_entry.status {0}", ebrEntry.status);
|
|
AaruConsole.DebugWriteLine(MODULE_NAME, "ebr_entry.type {0}", ebrEntry.type);
|
|
AaruConsole.DebugWriteLine(MODULE_NAME, "ebr_entry.lba_start {0}", ebrEntry.lba_start);
|
|
AaruConsole.DebugWriteLine(MODULE_NAME, "ebr_entry.lba_sectors {0}", ebrEntry.lba_sectors);
|
|
AaruConsole.DebugWriteLine(MODULE_NAME, "ebr_entry.start_cylinder {0}", startCylinder);
|
|
AaruConsole.DebugWriteLine(MODULE_NAME, "ebr_entry.start_head {0}", ebrEntry.start_head);
|
|
AaruConsole.DebugWriteLine(MODULE_NAME, "ebr_entry.start_sector {0}", startSector);
|
|
AaruConsole.DebugWriteLine(MODULE_NAME, "ebr_entry.end_cylinder {0}", endCylinder);
|
|
AaruConsole.DebugWriteLine(MODULE_NAME, "ebr_entry.end_head {0}", ebrEntry.end_head);
|
|
AaruConsole.DebugWriteLine(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;
|
|
|
|
AaruConsole.DebugWriteLine(MODULE_NAME, "ext_start {0}", extStart);
|
|
AaruConsole.DebugWriteLine(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<Partition> 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);
|
|
}
|
|
|
|
AaruConsole.DebugWriteLine(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
|
|
|
|
static bool GetMinix(IMediaImage imagePlugin, ulong start, ulong divider, ulong sectorOffset, uint sectorSize,
|
|
out List<Partition> partitions)
|
|
{
|
|
partitions = new List<Partition>();
|
|
|
|
ErrorNumber errno = imagePlugin.ReadSector(start, out byte[] sector);
|
|
|
|
if(errno != ErrorNumber.NoError)
|
|
return false;
|
|
|
|
ExtendedBootRecord mnx = Marshal.ByteArrayToStructureLittleEndian<ExtendedBootRecord>(sector);
|
|
|
|
AaruConsole.DebugWriteLine(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;
|
|
|
|
AaruConsole.DebugWriteLine(MODULE_NAME, "mnx_entry.status {0}", mnxEntry.status);
|
|
AaruConsole.DebugWriteLine(MODULE_NAME, "mnx_entry.type {0}", mnxEntry.type);
|
|
AaruConsole.DebugWriteLine(MODULE_NAME, "mnx_entry.lba_start {0}", mnxEntry.lba_start);
|
|
AaruConsole.DebugWriteLine(MODULE_NAME, "mnx_entry.lba_sectors {0}", mnxEntry.lba_sectors);
|
|
AaruConsole.DebugWriteLine(MODULE_NAME, "mnx_entry.start_cylinder {0}", startCylinder);
|
|
AaruConsole.DebugWriteLine(MODULE_NAME, "mnx_entry.start_head {0}", mnxEntry.start_head);
|
|
AaruConsole.DebugWriteLine(MODULE_NAME, "mnx_entry.start_sector {0}", startSector);
|
|
AaruConsole.DebugWriteLine(MODULE_NAME, "mnx_entry.end_cylinder {0}", endCylinder);
|
|
AaruConsole.DebugWriteLine(MODULE_NAME, "mnx_entry.end_head {0}", mnxEntry.end_head);
|
|
AaruConsole.DebugWriteLine(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;
|
|
|
|
AaruConsole.DebugWriteLine(MODULE_NAME, "mnx_start {0}", mnxStart);
|
|
AaruConsole.DebugWriteLine(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
|
|
{
|
|
/// <summary>Boot code</summary>
|
|
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 252)]
|
|
public readonly byte[] boot_code;
|
|
/// <summary>
|
|
/// <see cref="MBR.DM_MAGIC" />
|
|
/// </summary>
|
|
public readonly ushort dm_magic;
|
|
/// <summary>Partitions</summary>
|
|
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
|
|
public readonly PartitionEntry[] entries;
|
|
/// <summary>
|
|
/// <see cref="MBR.MBR_MAGIC" />
|
|
/// </summary>
|
|
public readonly ushort magic;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Nested type: ExtendedBootRecord
|
|
|
|
// TODO: IBM Boot Manager
|
|
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
|
readonly struct ExtendedBootRecord
|
|
{
|
|
/// <summary>Boot code, almost always unused</summary>
|
|
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 446)]
|
|
public readonly byte[] boot_code;
|
|
/// <summary>Partitions or pointers</summary>
|
|
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
|
|
public readonly PartitionEntry[] entries;
|
|
/// <summary>
|
|
/// <see cref="MBR.MBR_MAGIC" />
|
|
/// </summary>
|
|
public readonly ushort magic;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Nested type: MasterBootRecord
|
|
|
|
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
|
readonly struct MasterBootRecord
|
|
{
|
|
/// <summary>Boot code</summary>
|
|
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 446)]
|
|
public readonly byte[] boot_code;
|
|
/// <summary>Partitions</summary>
|
|
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
|
|
public readonly PartitionEntry[] entries;
|
|
/// <summary>
|
|
/// <see cref="MBR.MBR_MAGIC" />
|
|
/// </summary>
|
|
public readonly ushort magic;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Nested type: ModernMasterBootRecord
|
|
|
|
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
|
readonly struct ModernMasterBootRecord
|
|
{
|
|
/// <summary>Boot code</summary>
|
|
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 218)]
|
|
public readonly byte[] boot_code;
|
|
/// <summary>Set to 0</summary>
|
|
public readonly ushort zero;
|
|
/// <summary>Original physical drive</summary>
|
|
public readonly byte drive;
|
|
/// <summary>Disk timestamp, seconds</summary>
|
|
public readonly byte seconds;
|
|
/// <summary>Disk timestamp, minutes</summary>
|
|
public readonly byte minutes;
|
|
/// <summary>Disk timestamp, hours</summary>
|
|
public readonly byte hours;
|
|
/// <summary>Boot code, continuation</summary>
|
|
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 216)]
|
|
public readonly byte[] boot_code2;
|
|
/// <summary>Disk serial number</summary>
|
|
public readonly uint serial;
|
|
/// <summary>Set to 0</summary>
|
|
public readonly ushort zero2;
|
|
/// <summary>Partitions</summary>
|
|
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
|
|
public readonly PartitionEntry[] entries;
|
|
/// <summary>
|
|
/// <see cref="MBR.MBR_MAGIC" />
|
|
/// </summary>
|
|
public readonly ushort magic;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Nested type: NecMasterBootRecord
|
|
|
|
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
|
readonly struct NecMasterBootRecord
|
|
{
|
|
/// <summary>Boot code</summary>
|
|
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 380)]
|
|
public readonly byte[] boot_code;
|
|
/// <summary>
|
|
/// <see cref="MBR.NEC_MAGIC" />
|
|
/// </summary>
|
|
public readonly ushort nec_magic;
|
|
/// <summary>Partitions</summary>
|
|
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
|
|
public readonly PartitionEntry[] entries;
|
|
/// <summary>
|
|
/// <see cref="MBR.MBR_MAGIC" />
|
|
/// </summary>
|
|
public readonly ushort magic;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Nested type: PartitionEntry
|
|
|
|
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
|
readonly struct PartitionEntry
|
|
{
|
|
/// <summary>Partition status, 0x80 or 0x00, else invalid</summary>
|
|
public readonly byte status;
|
|
/// <summary>Starting head [0,254]</summary>
|
|
public readonly byte start_head;
|
|
/// <summary>Starting sector [1,63]</summary>
|
|
public readonly byte start_sector;
|
|
/// <summary>Starting cylinder [0,1023]</summary>
|
|
public readonly byte start_cylinder;
|
|
/// <summary>Partition type</summary>
|
|
public readonly byte type;
|
|
/// <summary>Ending head [0,254]</summary>
|
|
public readonly byte end_head;
|
|
/// <summary>Ending sector [1,63]</summary>
|
|
public readonly byte end_sector;
|
|
/// <summary>Ending cylinder [0,1023]</summary>
|
|
public readonly byte end_cylinder;
|
|
/// <summary>Starting absolute sector</summary>
|
|
public readonly uint lba_start;
|
|
/// <summary>Total sectors</summary>
|
|
public readonly uint lba_sectors;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Nested type: SerializedMasterBootRecord
|
|
|
|
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
|
readonly struct SerializedMasterBootRecord
|
|
{
|
|
/// <summary>Boot code</summary>
|
|
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 440)]
|
|
public readonly byte[] boot_code;
|
|
/// <summary>Disk serial number</summary>
|
|
public readonly uint serial;
|
|
/// <summary>Set to 0</summary>
|
|
public readonly ushort zero;
|
|
/// <summary>Partitions</summary>
|
|
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
|
|
public readonly PartitionEntry[] entries;
|
|
/// <summary>
|
|
/// <see cref="MBR.MBR_MAGIC" />
|
|
/// </summary>
|
|
public readonly ushort magic;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Nested type: TimedMasterBootRecord
|
|
|
|
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
|
readonly struct TimedMasterBootRecord
|
|
{
|
|
/// <summary>Boot code</summary>
|
|
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 218)]
|
|
public readonly byte[] boot_code;
|
|
/// <summary>Set to 0</summary>
|
|
public readonly ushort zero;
|
|
/// <summary>Original physical drive</summary>
|
|
public readonly byte drive;
|
|
/// <summary>Disk timestamp, seconds</summary>
|
|
public readonly byte seconds;
|
|
/// <summary>Disk timestamp, minutes</summary>
|
|
public readonly byte minutes;
|
|
/// <summary>Disk timestamp, hours</summary>
|
|
public readonly byte hours;
|
|
/// <summary>Boot code, continuation</summary>
|
|
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 222)]
|
|
public readonly byte[] boot_code2;
|
|
/// <summary>Partitions</summary>
|
|
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
|
|
public readonly PartitionEntry[] entries;
|
|
/// <summary>
|
|
/// <see cref="MBR.MBR_MAGIC" />
|
|
/// </summary>
|
|
public readonly ushort magic;
|
|
}
|
|
|
|
#endregion
|
|
} |