// /*************************************************************************** // Aaru Data Preservation Suite // ---------------------------------------------------------------------------- // // Filename : BSD.cs // Author(s) : Natalia Portillo // // Component : Partitioning scheme plugins. // // --[ Description ] ---------------------------------------------------------- // // Manages BSD disklabels. // // --[ 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-2023 Natalia Portillo // ****************************************************************************/ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; 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; /// /// Implements decoding of BSD disklabels [SuppressMessage("ReSharper", "UnusedMember.Local")] public sealed class BSD : IPartition { const uint DISK_MAGIC = 0x82564557; const uint DISK_CIGAM = 0x57455682; /// Maximum size of a disklabel with 22 partitions const uint MAX_LABEL_SIZE = 500; const string MODULE_NAME = "BSD disklabel plugin"; /// Known sector locations for BSD disklabel readonly ulong[] _labelLocations = { 0, 1, 2, 9 }; /// Known byte offsets for BSD disklabel readonly uint[] _labelOffsets = { 0, 9, 64, 128, 516 }; #region IPartition Members /// public string Name => Localization.BSD_Name; /// public Guid Id => new("246A6D93-4F1A-1F8A-344D-50187A5513A9"); /// public string Author => Authors.NATALIA_PORTILLO; /// public bool GetInformation(IMediaImage imagePlugin, out List partitions, ulong sectorOffset) { partitions = new List(); uint run = (MAX_LABEL_SIZE + _labelOffsets.Last()) / imagePlugin.Info.SectorSize; if((MAX_LABEL_SIZE + _labelOffsets.Last()) % imagePlugin.Info.SectorSize > 0) run++; var dl = new DiskLabel(); var found = false; foreach(ulong location in _labelLocations) { if(location + run + sectorOffset >= imagePlugin.Info.Sectors) return false; ErrorNumber errno = imagePlugin.ReadSectors(location + sectorOffset, run, out byte[] tmp); if(errno != ErrorNumber.NoError) continue; foreach(uint offset in _labelOffsets) { var sector = new byte[MAX_LABEL_SIZE]; if(offset + MAX_LABEL_SIZE > tmp.Length) break; Array.Copy(tmp, offset, sector, 0, MAX_LABEL_SIZE); dl = Marshal.ByteArrayToStructureLittleEndian(sector); AaruConsole.DebugWriteLine(MODULE_NAME, Localization. BSD_GetInformation_dl_magic_on_sector_0_at_offset_1_equals_2_X8_expected_3_X8, location + sectorOffset, offset, dl.d_magic, DISK_MAGIC); if((dl.d_magic != DISK_MAGIC || dl.d_magic2 != DISK_MAGIC) && (dl.d_magic != DISK_CIGAM || dl.d_magic2 != DISK_CIGAM)) continue; found = true; break; } if(found) break; } if(!found) return false; if(dl is { d_magic: DISK_CIGAM, d_magic2: DISK_CIGAM }) dl = SwapDiskLabel(dl); AaruConsole.DebugWriteLine(MODULE_NAME, "dl.d_type = {0}", dl.d_type); AaruConsole.DebugWriteLine(MODULE_NAME, "dl.d_subtype = {0}", dl.d_subtype); AaruConsole.DebugWriteLine(MODULE_NAME, "dl.d_typename = {0}", StringHandlers.CToString(dl.d_typename)); AaruConsole.DebugWriteLine(MODULE_NAME, "dl.d_packname = {0}", StringHandlers.CToString(dl.d_packname)); AaruConsole.DebugWriteLine(MODULE_NAME, "dl.d_secsize = {0}", dl.d_secsize); AaruConsole.DebugWriteLine(MODULE_NAME, "dl.d_nsectors = {0}", dl.d_nsectors); AaruConsole.DebugWriteLine(MODULE_NAME, "dl.d_ntracks = {0}", dl.d_ntracks); AaruConsole.DebugWriteLine(MODULE_NAME, "dl.d_ncylinders = {0}", dl.d_ncylinders); AaruConsole.DebugWriteLine(MODULE_NAME, "dl.d_secpercyl = {0}", dl.d_secpercyl); AaruConsole.DebugWriteLine(MODULE_NAME, "dl.d_secperunit = {0}", dl.d_secperunit); AaruConsole.DebugWriteLine(MODULE_NAME, "dl.d_sparespertrack = {0}", dl.d_sparespertrack); AaruConsole.DebugWriteLine(MODULE_NAME, "dl.d_sparespercyl = {0}", dl.d_sparespercyl); AaruConsole.DebugWriteLine(MODULE_NAME, "dl.d_acylinders = {0}", dl.d_acylinders); AaruConsole.DebugWriteLine(MODULE_NAME, "dl.d_rpm = {0}", dl.d_rpm); AaruConsole.DebugWriteLine(MODULE_NAME, "dl.d_interleave = {0}", dl.d_interleave); AaruConsole.DebugWriteLine(MODULE_NAME, "dl.d_trackskew = {0}", dl.d_trackskew); AaruConsole.DebugWriteLine(MODULE_NAME, "dl.d_cylskeew = {0}", dl.d_cylskeew); AaruConsole.DebugWriteLine(MODULE_NAME, "dl.d_headswitch = {0}", dl.d_headswitch); AaruConsole.DebugWriteLine(MODULE_NAME, "dl.d_trkseek = {0}", dl.d_trkseek); AaruConsole.DebugWriteLine(MODULE_NAME, "dl.d_flags = {0}", dl.d_flags); AaruConsole.DebugWriteLine(MODULE_NAME, "dl.d_drivedata[0] = {0}", dl.d_drivedata[0]); AaruConsole.DebugWriteLine(MODULE_NAME, "dl.d_drivedata[1] = {0}", dl.d_drivedata[1]); AaruConsole.DebugWriteLine(MODULE_NAME, "dl.d_drivedata[2] = {0}", dl.d_drivedata[2]); AaruConsole.DebugWriteLine(MODULE_NAME, "dl.d_drivedata[3] = {0}", dl.d_drivedata[3]); AaruConsole.DebugWriteLine(MODULE_NAME, "dl.d_drivedata[4] = {0}", dl.d_drivedata[4]); AaruConsole.DebugWriteLine(MODULE_NAME, "dl.d_spare[0] = {0}", dl.d_spare[0]); AaruConsole.DebugWriteLine(MODULE_NAME, "dl.d_spare[1] = {0}", dl.d_spare[1]); AaruConsole.DebugWriteLine(MODULE_NAME, "dl.d_spare[2] = {0}", dl.d_spare[2]); AaruConsole.DebugWriteLine(MODULE_NAME, "dl.d_spare[3] = {0}", dl.d_spare[3]); AaruConsole.DebugWriteLine(MODULE_NAME, "dl.d_spare[4] = {0}", dl.d_spare[4]); AaruConsole.DebugWriteLine(MODULE_NAME, "dl.d_magic2 = 0x{0:X8}", dl.d_magic2); AaruConsole.DebugWriteLine(MODULE_NAME, "dl.d_checksum = 0x{0:X8}", dl.d_checksum); AaruConsole.DebugWriteLine(MODULE_NAME, "dl.d_npartitions = {0}", dl.d_npartitions); AaruConsole.DebugWriteLine(MODULE_NAME, "dl.d_bbsize = {0}", dl.d_bbsize); AaruConsole.DebugWriteLine(MODULE_NAME, "dl.d_sbsize = {0}", dl.d_sbsize); ulong counter = 0; var addSectorOffset = false; for(var i = 0; i < dl.d_npartitions && i < 22; i++) { AaruConsole.DebugWriteLine(MODULE_NAME, "dl.d_partitions[i].p_offset = {0}", dl.d_partitions[i].p_offset); AaruConsole.DebugWriteLine(MODULE_NAME, "dl.d_partitions[i].p_size = {0}", dl.d_partitions[i].p_size); AaruConsole.DebugWriteLine(MODULE_NAME, "dl.d_partitions[i].p_fstype = {0} ({1})", dl.d_partitions[i].p_fstype, FSTypeToString(dl.d_partitions[i].p_fstype)); var part = new Partition { Start = dl.d_partitions[i].p_offset * dl.d_secsize / imagePlugin.Info.SectorSize, Offset = dl.d_partitions[i].p_offset * dl.d_secsize, Length = dl.d_partitions[i].p_size * dl.d_secsize / imagePlugin.Info.SectorSize, Size = dl.d_partitions[i].p_size * dl.d_secsize, Type = FSTypeToString(dl.d_partitions[i].p_fstype), Sequence = counter, Scheme = Name }; if(dl.d_partitions[i].p_fstype == fsType.Unused) continue; // Crude and dirty way to know if the disklabel is relative to its parent partition... if(dl.d_partitions[i].p_offset < sectorOffset && !addSectorOffset) addSectorOffset = true; if(addSectorOffset) { part.Start += sectorOffset; part.Offset += sectorOffset * imagePlugin.Info.SectorSize; } AaruConsole.DebugWriteLine(MODULE_NAME, "part.start = {0}", part.Start); AaruConsole.DebugWriteLine(MODULE_NAME, Localization.BSD_GetInformation_Adding_it); partitions.Add(part); counter++; } return partitions.Count > 0; } #endregion internal static string FSTypeToString(fsType typ) { switch(typ) { case fsType.Unused: return Localization.Unused_entry; case fsType.Swap: return Localization.Swap_partition; case fsType.V6: return Localization.UNIX_6th_Edition; case fsType.V7: return Localization.UNIX_7th_Edition; case fsType.SystemV: return Localization.UNIX_System_V; case fsType.V7_1K: return Localization.UNIX_7th_Edition_with_1K_blocks; case fsType.V8: return Localization.UNIX_8th_Edition_with_4K_blocks; case fsType.BSDFFS: return Localization._4_2_BSD_Fast_File_System; case fsType.BSDLFS: return Localization._4_4_LFS; case fsType.HPFS: return Localization.HPFS; case fsType.ISO9660: return Localization.ISO9660; case fsType.Boot: case fsType.SysVBoot: return Localization.Boot; case fsType.AFFS: return Localization.Amiga_FFS; case fsType.HFS: return Localization.Apple_HFS; case fsType.ADVfs: return Localization.Digital_Advanced_File_System; case fsType.LSMpublic: return Localization.Digital_LSM_Public_Region; case fsType.LSMprivate: return Localization.Digital_LSM_Private_Region; case fsType.LSMsimple: return Localization.Digital_LSM_Simple_Disk; case fsType.CCD: return Localization.Concatenated_disk; case fsType.JFS2: return Localization.IBM_JFS2; case fsType.HAMMER: return Localization.Hammer; case fsType.HAMMER2: return Localization.Hammer2; case fsType.UDF: return Localization.UDF; case fsType.EFS: return Localization.EFS; case fsType.ZFS: return Localization.ZFS; case fsType.NANDFS: return Localization.FreeBSD_nandfs; case fsType.MSDOS: return Localization.FAT; case fsType.Other: return Localization.Other_or_unknown; default: return Localization.Unknown_partition_type; } } static DiskLabel SwapDiskLabel(DiskLabel dl) { dl = (DiskLabel)Marshal.SwapStructureMembersEndian(dl); for(var i = 0; i < dl.d_drivedata.Length; i++) dl.d_drivedata[i] = Swapping.Swap(dl.d_drivedata[i]); for(var i = 0; i < dl.d_spare.Length; i++) dl.d_spare[i] = Swapping.Swap(dl.d_spare[i]); for(var i = 0; i < dl.d_partitions.Length; i++) dl.d_partitions[i] = (BSDPartition)Marshal.SwapStructureMembersEndian(dl.d_partitions[i]); return dl; } #region Nested type: BSDPartition [StructLayout(LayoutKind.Sequential, Pack = 1)] struct BSDPartition { /// Sectors in partition public readonly uint p_size; /// Starting sector public readonly uint p_offset; /// Fragment size public readonly uint p_fsize; /// Filesystem type, public readonly fsType p_fstype; /// Fragment size public readonly byte p_frag; /// Cylinder per group public readonly ushort p_cpg; } #endregion #region Nested type: dFlags /// Drive flags [SuppressMessage("ReSharper", "InconsistentNaming")] [Flags] enum dFlags : uint { /// Removable media Removable = 0x01, /// Drive supports ECC ECC = 0x02, /// Drive supports bad sector forwarding BadSectorForward = 0x04, /// Disk emulator RAMDisk = 0x08, /// Can do back to back transfer Chain = 0x10, /// Dynamic geometry device DynamicGeometry = 0x20 } #endregion #region Nested type: DiskLabel [StructLayout(LayoutKind.Sequential, Pack = 1)] struct DiskLabel { /// /// /// public readonly uint d_magic; /// /// /// public readonly dType d_type; /// Disk subtype public readonly ushort d_subtype; /// Type name [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] public readonly byte[] d_typename; /// Pack identifier [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] public readonly byte[] d_packname; /// Bytes per sector public readonly uint d_secsize; /// Sectors per track public readonly uint d_nsectors; /// Tracks per cylinder public readonly uint d_ntracks; /// Cylinders per unit public readonly uint d_ncylinders; /// Sectors per cylinder public readonly uint d_secpercyl; /// Sectors per unit public readonly uint d_secperunit; /// Spare sectors per track public readonly ushort d_sparespertrack; /// Spare sectors per cylinder public readonly ushort d_sparespercyl; /// Alternate cylinders public readonly uint d_acylinders; /// Rotational speed public readonly ushort d_rpm; /// Hardware sector interleave public readonly ushort d_interleave; /// Sector 0 skew per track public readonly ushort d_trackskew; /// Sector 0 sker per cylinder public readonly ushort d_cylskeew; /// Head switch time in microseconds public readonly uint d_headswitch; /// Track to track seek in microseconds public readonly uint d_trkseek; /// /// /// public readonly dFlags d_flags; /// Drive-specific information [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)] public readonly uint[] d_drivedata; /// Reserved [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)] public readonly uint[] d_spare; /// again public readonly uint d_magic2; /// XOR of data public readonly ushort d_checksum; /// How many partitions public readonly ushort d_npartitions; /// Size of boot area in bytes public readonly uint d_bbsize; /// Maximum size of superblock in bytes public readonly uint d_sbsize; /// Partitions [MarshalAs(UnmanagedType.ByValArray, SizeConst = 22)] public readonly BSDPartition[] d_partitions; } #endregion #region Nested type: dType /// Drive type [SuppressMessage("ReSharper", "InconsistentNaming")] enum dType : ushort { /// SMD, XSMD SMD = 1, /// MSCP MSCP = 2, /// Other DEC (rk, rl) DEC = 3, /// SCSI SCSI = 4, /// ESDI ESDI = 5, /// ST506 et al ST506 = 6, /// CS/80 on HP-IB HPIB = 7, /// HP Fiber-link HPFL = 8, /// Floppy FLOPPY = 10, /// Concatenated disk CCD = 11, /// uvnode pseudo-disk VND = 12, /// DiskOnChip DOC2K = 13, /// ATAPI ATAPI = 13, /// CMU RAIDframe RAID = 14, /// Logical disk LD = 15, /// IBM JFS 2 JFS2 = 16, /// Cryptographic pseudo-disk CGD = 17, /// Vinum volume VINUM = 18, /// Flash memory devices FLASH = 19, /// Device-mapper pseudo-disk devices DM = 20, /// Rump virtual disk RUMPD = 21, /// Memory disk MD = 22 } #endregion #region Nested type: fsType /// Filesystem type [SuppressMessage("ReSharper", "InconsistentNaming")] [SuppressMessage("ReSharper", "UnusedMember.Global")] internal enum fsType : byte { /// Unused entry Unused = 0, /// Swap partition Swap = 1, /// UNIX 6th Edition V6 = 2, /// UNIX 7th Edition V7 = 3, /// UNIX System V SystemV = 4, /// UNIX 7th Edition with 1K blocks V7_1K = 5, /// UNIX 8th Edition with 4K blocks V8 = 6, /// 4.2BSD Fast File System BSDFFS = 7, /// MS-DOS filesystem MSDOS = 8, /// 4.4LFS BSDLFS = 9, /// In use, unknown or unsupported Other = 10, /// HPFS HPFS = 11, /// ISO9660 ISO9660 = 12, /// Boot partition Boot = 13, /// Amiga FFS AFFS = 14, /// Apple HFS HFS = 15, /// Acorn ADFS FileCore = 16, /// Digital Advanced File System ADVfs = 16, /// Digital LSM Public Region LSMpublic = 17, /// Linux ext2 ext2 = 17, /// Digital LSM Private Region LSMprivate = 18, /// NTFS NTFS = 18, /// Digital LSM Simple Disk LSMsimple = 19, /// RAIDframe component RAID = 19, /// Concatenated disk component CCD = 20, /// IBM JFS2 JFS2 = 21, /// Apple UFS AppleUFS = 22, /// Hammer filesystem HAMMER = 22, /// Hammer2 filesystem HAMMER2 = 23, /// UDF UDF = 24, /// System V Boot filesystem SysVBoot = 25, /// EFS EFS = 26, /// ZFS ZFS = 27, /// NiLFS NILFS = 27, /// Cryptographic disk CGD = 28, /// MINIX v3 MINIX = 29, /// FreeBSD nandfs NANDFS = 30 } #endregion }