// /*************************************************************************** // 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-2021 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; /// 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 }; /// public string Name => "BSD disklabel"; /// public Guid Id => new("246A6D93-4F1A-1F8A-344D-50187A5513A9"); /// public string Author => "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(); bool 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) { byte[] 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("BSD plugin", "dl.magic on sector {0} at offset {1} = 0x{2:X8} (expected 0x{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.d_magic == DISK_CIGAM && dl.d_magic2 == DISK_CIGAM) dl = SwapDiskLabel(dl); AaruConsole.DebugWriteLine("BSD plugin", "dl.d_type = {0}", dl.d_type); AaruConsole.DebugWriteLine("BSD plugin", "dl.d_subtype = {0}", dl.d_subtype); AaruConsole.DebugWriteLine("BSD plugin", "dl.d_typename = {0}", StringHandlers.CToString(dl.d_typename)); AaruConsole.DebugWriteLine("BSD plugin", "dl.d_packname = {0}", StringHandlers.CToString(dl.d_packname)); AaruConsole.DebugWriteLine("BSD plugin", "dl.d_secsize = {0}", dl.d_secsize); AaruConsole.DebugWriteLine("BSD plugin", "dl.d_nsectors = {0}", dl.d_nsectors); AaruConsole.DebugWriteLine("BSD plugin", "dl.d_ntracks = {0}", dl.d_ntracks); AaruConsole.DebugWriteLine("BSD plugin", "dl.d_ncylinders = {0}", dl.d_ncylinders); AaruConsole.DebugWriteLine("BSD plugin", "dl.d_secpercyl = {0}", dl.d_secpercyl); AaruConsole.DebugWriteLine("BSD plugin", "dl.d_secperunit = {0}", dl.d_secperunit); AaruConsole.DebugWriteLine("BSD plugin", "dl.d_sparespertrack = {0}", dl.d_sparespertrack); AaruConsole.DebugWriteLine("BSD plugin", "dl.d_sparespercyl = {0}", dl.d_sparespercyl); AaruConsole.DebugWriteLine("BSD plugin", "dl.d_acylinders = {0}", dl.d_acylinders); AaruConsole.DebugWriteLine("BSD plugin", "dl.d_rpm = {0}", dl.d_rpm); AaruConsole.DebugWriteLine("BSD plugin", "dl.d_interleave = {0}", dl.d_interleave); AaruConsole.DebugWriteLine("BSD plugin", "dl.d_trackskew = {0}", dl.d_trackskew); AaruConsole.DebugWriteLine("BSD plugin", "dl.d_cylskeew = {0}", dl.d_cylskeew); AaruConsole.DebugWriteLine("BSD plugin", "dl.d_headswitch = {0}", dl.d_headswitch); AaruConsole.DebugWriteLine("BSD plugin", "dl.d_trkseek = {0}", dl.d_trkseek); AaruConsole.DebugWriteLine("BSD plugin", "dl.d_flags = {0}", dl.d_flags); AaruConsole.DebugWriteLine("BSD plugin", "dl.d_drivedata[0] = {0}", dl.d_drivedata[0]); AaruConsole.DebugWriteLine("BSD plugin", "dl.d_drivedata[1] = {0}", dl.d_drivedata[1]); AaruConsole.DebugWriteLine("BSD plugin", "dl.d_drivedata[2] = {0}", dl.d_drivedata[2]); AaruConsole.DebugWriteLine("BSD plugin", "dl.d_drivedata[3] = {0}", dl.d_drivedata[3]); AaruConsole.DebugWriteLine("BSD plugin", "dl.d_drivedata[4] = {0}", dl.d_drivedata[4]); AaruConsole.DebugWriteLine("BSD plugin", "dl.d_spare[0] = {0}", dl.d_spare[0]); AaruConsole.DebugWriteLine("BSD plugin", "dl.d_spare[1] = {0}", dl.d_spare[1]); AaruConsole.DebugWriteLine("BSD plugin", "dl.d_spare[2] = {0}", dl.d_spare[2]); AaruConsole.DebugWriteLine("BSD plugin", "dl.d_spare[3] = {0}", dl.d_spare[3]); AaruConsole.DebugWriteLine("BSD plugin", "dl.d_spare[4] = {0}", dl.d_spare[4]); AaruConsole.DebugWriteLine("BSD plugin", "dl.d_magic2 = 0x{0:X8}", dl.d_magic2); AaruConsole.DebugWriteLine("BSD plugin", "dl.d_checksum = 0x{0:X8}", dl.d_checksum); AaruConsole.DebugWriteLine("BSD plugin", "dl.d_npartitions = {0}", dl.d_npartitions); AaruConsole.DebugWriteLine("BSD plugin", "dl.d_bbsize = {0}", dl.d_bbsize); AaruConsole.DebugWriteLine("BSD plugin", "dl.d_sbsize = {0}", dl.d_sbsize); ulong counter = 0; bool addSectorOffset = false; for(int i = 0; i < dl.d_npartitions && i < 22; i++) { AaruConsole.DebugWriteLine("BSD plugin", "dl.d_partitions[i].p_offset = {0}", dl.d_partitions[i].p_offset); AaruConsole.DebugWriteLine("BSD plugin", "dl.d_partitions[i].p_size = {0}", dl.d_partitions[i].p_size); AaruConsole.DebugWriteLine("BSD plugin", "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("BSD plugin", "part.start = {0}", part.Start); AaruConsole.DebugWriteLine("BSD plugin", "Adding it..."); partitions.Add(part); counter++; } return partitions.Count > 0; } internal static string FSTypeToString(fsType typ) { switch(typ) { case fsType.Unused: return "Unused entry"; case fsType.Swap: return "Swap partition"; case fsType.V6: return "UNIX 6th Edition"; case fsType.V7: return "UNIX 7th Edition"; case fsType.SystemV: return "UNIX System V"; case fsType.V7_1K: return "UNIX 7th Edition with 1K blocks"; case fsType.V8: return "UNIX 8th Edition with 4K blocks"; case fsType.BSDFFS: return "4.2BSD Fast File System"; case fsType.BSDLFS: return "4.4LFS"; case fsType.HPFS: return "HPFS"; case fsType.ISO9660: return "ISO9660"; case fsType.Boot: case fsType.SysVBoot: return "Boot"; case fsType.AFFS: return "Amiga FFS"; case fsType.HFS: return "Apple HFS"; case fsType.ADVfs: return "Digital Advanced File System"; case fsType.LSMpublic: return "Digital LSM Public Region"; case fsType.LSMprivate: return "Digital LSM Private Region"; case fsType.LSMsimple: return "Digital LSM Simple Disk"; case fsType.CCD: return "Concatenated disk"; case fsType.JFS2: return "IBM JFS2"; case fsType.HAMMER: return "Hammer"; case fsType.HAMMER2: return "Hammer2"; case fsType.UDF: return "UDF"; case fsType.EFS: return "EFS"; case fsType.ZFS: return "ZFS"; case fsType.NANDFS: return "FreeBSD nandfs"; case fsType.MSDOS: return "FAT"; case fsType.Other: return "Other or unknown"; default: return "Unknown"; } } static DiskLabel SwapDiskLabel(DiskLabel dl) { dl = (DiskLabel)Marshal.SwapStructureMembersEndian(dl); for(int i = 0; i < dl.d_drivedata.Length; i++) dl.d_drivedata[i] = Swapping.Swap(dl.d_drivedata[i]); for(int i = 0; i < dl.d_spare.Length; i++) dl.d_spare[i] = Swapping.Swap(dl.d_spare[i]); for(int i = 0; i < dl.d_partitions.Length; i++) dl.d_partitions[i] = (BSDPartition)Marshal.SwapStructureMembersEndian(dl.d_partitions[i]); return dl; } /// 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 } /// Filesystem type [SuppressMessage("ReSharper", "InconsistentNaming")] 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 } /// 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 } [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; } [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; } } }