// /*************************************************************************** // The Disc Image Chef // ---------------------------------------------------------------------------- // // 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-2018 Natalia Portillo // ****************************************************************************/ using System; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; using DiscImageChef.CommonTypes; using DiscImageChef.Console; using DiscImageChef.DiscImages; namespace DiscImageChef.Partitions { public class BSD : PartitionPlugin { const uint DISKMAGIC = 0x82564557; const uint DISKCIGAM = 0x57455682; /// 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}; /// Maximum size of a disklabel with 22 partitions const uint maxLabelSize = 500; public BSD() { Name = "BSD disklabel"; PluginUuid = new Guid("246A6D93-4F1A-1F8A-344D-50187A5513A9"); } public override bool GetInformation(ImagePlugin imagePlugin, out List partitions, ulong sectorOffset) { partitions = new List(); uint run = (maxLabelSize + labelOffsets.Last()) / imagePlugin.GetSectorSize(); if((maxLabelSize + labelOffsets.Last()) % imagePlugin.GetSectorSize() > 0) run++; byte[] sector; DiskLabel dl = new DiskLabel(); bool found = false; foreach(ulong location in labelLocations) { if(location + run + sectorOffset >= imagePlugin.GetSectors()) return false; byte[] tmp = imagePlugin.ReadSectors(location + sectorOffset, run); foreach(uint offset in labelOffsets) { sector = new byte[maxLabelSize]; Array.Copy(tmp, offset, sector, 0, maxLabelSize); dl = GetDiskLabel(sector); DicConsole.DebugWriteLine("BSD plugin", "dl.magic on sector {0} at offset {1} = 0x{2:X8} (expected 0x{3:X8})", location + sectorOffset, offset, dl.d_magic, DISKMAGIC); if((dl.d_magic != DISKMAGIC || dl.d_magic2 != DISKMAGIC) && (dl.d_magic != DISKCIGAM || dl.d_magic2 != DISKCIGAM)) continue; found = true; break; } if(found) break; } if(!found) return false; if(dl.d_magic == DISKCIGAM && dl.d_magic2 == DISKCIGAM) dl = SwapDiskLabel(dl); DicConsole.DebugWriteLine("BSD plugin", "dl.d_type = {0}", dl.d_type); DicConsole.DebugWriteLine("BSD plugin", "dl.d_subtype = {0}", dl.d_subtype); DicConsole.DebugWriteLine("BSD plugin", "dl.d_typename = {0}", StringHandlers.CToString(dl.d_typename)); DicConsole.DebugWriteLine("BSD plugin", "dl.d_packname = {0}", StringHandlers.CToString(dl.d_packname)); DicConsole.DebugWriteLine("BSD plugin", "dl.d_secsize = {0}", dl.d_secsize); DicConsole.DebugWriteLine("BSD plugin", "dl.d_nsectors = {0}", dl.d_nsectors); DicConsole.DebugWriteLine("BSD plugin", "dl.d_ntracks = {0}", dl.d_ntracks); DicConsole.DebugWriteLine("BSD plugin", "dl.d_ncylinders = {0}", dl.d_ncylinders); DicConsole.DebugWriteLine("BSD plugin", "dl.d_secpercyl = {0}", dl.d_secpercyl); DicConsole.DebugWriteLine("BSD plugin", "dl.d_secperunit = {0}", dl.d_secperunit); DicConsole.DebugWriteLine("BSD plugin", "dl.d_sparespertrack = {0}", dl.d_sparespertrack); DicConsole.DebugWriteLine("BSD plugin", "dl.d_sparespercyl = {0}", dl.d_sparespercyl); DicConsole.DebugWriteLine("BSD plugin", "dl.d_acylinders = {0}", dl.d_acylinders); DicConsole.DebugWriteLine("BSD plugin", "dl.d_rpm = {0}", dl.d_rpm); DicConsole.DebugWriteLine("BSD plugin", "dl.d_interleave = {0}", dl.d_interleave); DicConsole.DebugWriteLine("BSD plugin", "dl.d_trackskew = {0}", dl.d_trackskew); DicConsole.DebugWriteLine("BSD plugin", "dl.d_cylskeew = {0}", dl.d_cylskeew); DicConsole.DebugWriteLine("BSD plugin", "dl.d_headswitch = {0}", dl.d_headswitch); DicConsole.DebugWriteLine("BSD plugin", "dl.d_trkseek = {0}", dl.d_trkseek); DicConsole.DebugWriteLine("BSD plugin", "dl.d_flags = {0}", dl.d_flags); DicConsole.DebugWriteLine("BSD plugin", "dl.d_drivedata[0] = {0}", dl.d_drivedata[0]); DicConsole.DebugWriteLine("BSD plugin", "dl.d_drivedata[1] = {0}", dl.d_drivedata[1]); DicConsole.DebugWriteLine("BSD plugin", "dl.d_drivedata[2] = {0}", dl.d_drivedata[2]); DicConsole.DebugWriteLine("BSD plugin", "dl.d_drivedata[3] = {0}", dl.d_drivedata[3]); DicConsole.DebugWriteLine("BSD plugin", "dl.d_drivedata[4] = {0}", dl.d_drivedata[4]); DicConsole.DebugWriteLine("BSD plugin", "dl.d_spare[0] = {0}", dl.d_spare[0]); DicConsole.DebugWriteLine("BSD plugin", "dl.d_spare[1] = {0}", dl.d_spare[1]); DicConsole.DebugWriteLine("BSD plugin", "dl.d_spare[2] = {0}", dl.d_spare[2]); DicConsole.DebugWriteLine("BSD plugin", "dl.d_spare[3] = {0}", dl.d_spare[3]); DicConsole.DebugWriteLine("BSD plugin", "dl.d_spare[4] = {0}", dl.d_spare[4]); DicConsole.DebugWriteLine("BSD plugin", "dl.d_magic2 = 0x{0:X8}", dl.d_magic2); DicConsole.DebugWriteLine("BSD plugin", "dl.d_checksum = 0x{0:X8}", dl.d_checksum); DicConsole.DebugWriteLine("BSD plugin", "dl.d_npartitions = {0}", dl.d_npartitions); DicConsole.DebugWriteLine("BSD plugin", "dl.d_bbsize = {0}", dl.d_bbsize); DicConsole.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++) { DicConsole.DebugWriteLine("BSD plugin", "dl.d_partitions[i].p_offset = {0}", dl.d_partitions[i].p_offset); DicConsole.DebugWriteLine("BSD plugin", "dl.d_partitions[i].p_size = {0}", dl.d_partitions[i].p_size); DicConsole.DebugWriteLine("BSD plugin", "dl.d_partitions[i].p_fstype = {0} ({1})", dl.d_partitions[i].p_fstype, fsTypeToString(dl.d_partitions[i].p_fstype)); Partition part = new Partition { Start = dl.d_partitions[i].p_offset * dl.d_secsize / imagePlugin.GetSectorSize(), Offset = dl.d_partitions[i].p_offset * dl.d_secsize, Length = dl.d_partitions[i].p_size * dl.d_secsize / imagePlugin.GetSectorSize(), 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.GetSectorSize(); } DicConsole.DebugWriteLine("BSD plugin", "part.start = {0}", part.Start); DicConsole.DebugWriteLine("BSD plugin", "Adding it..."); partitions.Add(part); counter++; } return partitions.Count > 0; } /// Drive type 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 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 /// [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 uint d_magic; /// public dType d_type; /// Disk subtype public ushort d_subtype; /// Type name [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] public byte[] d_typename; /// Pack identifier [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] public byte[] d_packname; /// Bytes per sector public uint d_secsize; /// Sectors per track public uint d_nsectors; /// Tracks per cylinder public uint d_ntracks; /// Cylinders per unit public uint d_ncylinders; /// Sectors per cylinder public uint d_secpercyl; /// Sectors per unit public uint d_secperunit; /// Spare sectors per track public ushort d_sparespertrack; /// Spare sectors per cylinder public ushort d_sparespercyl; /// Alternate cylinders public uint d_acylinders; /// Rotational speed public ushort d_rpm; /// Hardware sector interleave public ushort d_interleave; /// Sector 0 skew per track public ushort d_trackskew; /// Sector 0 sker per cylinder public ushort d_cylskeew; /// Head switch time in microseconds public uint d_headswitch; /// Track to track seek in microseconds public uint d_trkseek; /// public dFlags d_flags; /// Drive-specific information [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)] public uint[] d_drivedata; /// Reserved [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)] /// public uint[] d_spare; /// again public uint d_magic2; /// XOR of data public ushort d_checksum; /// How many partitions public ushort d_npartitions; /// Size of boot area in bytes public uint d_bbsize; /// Maximum size of superblock in bytes public uint d_sbsize; /// Partitions [MarshalAs(UnmanagedType.ByValArray, SizeConst = 22)] public BSDPartition[] d_partitions; } [StructLayout(LayoutKind.Sequential, Pack = 1)] struct BSDPartition { /// Sectors in partition public uint p_size; /// Starting sector public uint p_offset; /// Fragment size public uint p_fsize; /// Filesystem type, public fsType p_fstype; /// Fragment size public byte p_frag; /// Cylinder per group public ushort p_cpg; } 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 GetDiskLabel(byte[] disklabel) { GCHandle handle = GCHandle.Alloc(disklabel, GCHandleType.Pinned); DiskLabel dl = (DiskLabel)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(DiskLabel)); handle.Free(); return dl; } static DiskLabel SwapDiskLabel(DiskLabel disklabel) { DiskLabel dl = BigEndianMarshal.SwapStructureMembersEndian(disklabel); 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] = BigEndianMarshal.SwapStructureMembersEndian(dl.d_partitions[i]); return dl; } } }