Files
Aaru/DiscImageChef.Partitions/BSD.cs

467 lines
21 KiB
C#

// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : BSD.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// 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 <http://www.gnu.org/licenses/>.
//
// ----------------------------------------------------------------------------
// Copyright © 2011-2019 Natalia Portillo
// ****************************************************************************/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using DiscImageChef.CommonTypes;
using DiscImageChef.CommonTypes.Interfaces;
using DiscImageChef.Console;
namespace DiscImageChef.Partitions
{
public class BSD : IPartition
{
const uint DISKMAGIC = 0x82564557;
const uint DISKCIGAM = 0x57455682;
/// <summary>Maximum size of a disklabel with 22 partitions</summary>
const uint MAX_LABEL_SIZE = 500;
/// <summary>Known sector locations for BSD disklabel</summary>
readonly ulong[] labelLocations = {0, 1, 2, 9};
/// <summary>Known byte offsets for BSD disklabel</summary>
readonly uint[] labelOffsets = {0, 9, 64, 128, 516};
public string Name => "BSD disklabel";
public Guid Id => new Guid("246A6D93-4F1A-1F8A-344D-50187A5513A9");
public string Author => "Natalia Portillo";
public bool GetInformation(IMediaImage imagePlugin, out List<Partition> partitions, ulong sectorOffset)
{
partitions = new List<Partition>();
uint run = (MAX_LABEL_SIZE + labelOffsets.Last()) / imagePlugin.Info.SectorSize;
if((MAX_LABEL_SIZE + labelOffsets.Last()) % imagePlugin.Info.SectorSize > 0) run++;
DiskLabel dl = new DiskLabel();
bool found = false;
foreach(ulong location in labelLocations)
{
if(location + run + sectorOffset >= imagePlugin.Info.Sectors) return false;
byte[] tmp = imagePlugin.ReadSectors(location + sectorOffset, run);
foreach(uint offset in labelOffsets)
{
byte[] sector = new byte[MAX_LABEL_SIZE];
Array.Copy(tmp, offset, sector, 0, MAX_LABEL_SIZE);
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.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;
}
DicConsole.DebugWriteLine("BSD plugin", "part.start = {0}", part.Start);
DicConsole.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 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 dl)
{
dl = (DiskLabel)Helpers.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)Helpers.Marshal.SwapStructureMembersEndian(dl.d_partitions[i]);
return dl;
}
/// <summary>Drive type</summary>
enum dType : ushort
{
/// <summary>SMD, XSMD</summary>
SMD = 1,
/// <summary>MSCP</summary>
MSCP = 2,
/// <summary>Other DEC (rk, rl)</summary>
DEC = 3,
/// <summary>SCSI</summary>
SCSI = 4,
/// <summary>ESDI</summary>
ESDI = 5,
/// <summary>ST506 et al</summary>
ST506 = 6,
/// <summary>CS/80 on HP-IB</summary>
HPIB = 7,
/// <summary>HP Fiber-link</summary>
HPFL = 8,
/// <summary>Floppy</summary>
FLOPPY = 10,
/// <summary>Concatenated disk</summary>
CCD = 11,
/// <summary>uvnode pseudo-disk</summary>
VND = 12,
/// <summary>DiskOnChip</summary>
DOC2K = 13,
/// <summary>ATAPI</summary>
ATAPI = 13,
/// <summary>CMU RAIDframe</summary>
RAID = 14,
/// <summary>Logical disk</summary>
LD = 15,
/// <summary>IBM JFS 2</summary>
JFS2 = 16,
/// <summary>Cryptographic pseudo-disk</summary>
CGD = 17,
/// <summary>Vinum volume</summary>
VINUM = 18,
/// <summary>Flash memory devices</summary>
FLASH = 19,
/// <summary>Device-mapper pseudo-disk devices</summary>
DM = 20,
/// <summary>Rump virtual disk</summary>
RUMPD = 21,
/// <summary>Memory disk</summary>
MD = 22
}
/// <summary>Filesystem type</summary>
internal enum fsType : byte
{
/// <summary>Unused entry</summary>
Unused = 0,
/// <summary>Swap partition</summary>
Swap = 1,
/// <summary>UNIX 6th Edition</summary>
V6 = 2,
/// <summary>UNIX 7th Edition</summary>
V7 = 3,
/// <summary>UNIX System V</summary>
SystemV = 4,
/// <summary>UNIX 7th Edition with 1K blocks</summary>
V7_1K = 5,
/// <summary>UNIX 8th Edition with 4K blocks</summary>
V8 = 6,
/// <summary>4.2BSD Fast File System</summary>
BSDFFS = 7,
/// <summary>MS-DOS filesystem</summary>
MSDOS = 8,
/// <summary>4.4LFS</summary>
BSDLFS = 9,
/// <summary>In use, unknown or unsupported</summary>
Other = 10,
/// <summary>HPFS</summary>
HPFS = 11,
/// <summary>ISO9660</summary>
ISO9660 = 12,
/// <summary>Boot partition</summary>
Boot = 13,
/// <summary>Amiga FFS</summary>
AFFS = 14,
/// <summary>Apple HFS</summary>
HFS = 15,
/// <summary>Acorn ADFS</summary>
FileCore = 16,
/// <summary>Digital Advanced File System</summary>
ADVfs = 16,
/// <summary>Digital LSM Public Region</summary>
LSMpublic = 17,
/// <summary>Linux ext2</summary>
ext2 = 17,
/// <summary>Digital LSM Private Region</summary>
LSMprivate = 18,
/// <summary>NTFS</summary>
NTFS = 18,
/// <summary>Digital LSM Simple Disk</summary>
LSMsimple = 19,
/// <summary>RAIDframe component</summary>
RAID = 19,
/// <summary>Concatenated disk component</summary>
CCD = 20,
/// <summary>IBM JFS2</summary>
JFS2 = 21,
/// <summary>Apple UFS</summary>
AppleUFS = 22,
/// <summary>Hammer filesystem</summary>
HAMMER = 22,
/// <summary>Hammer2 filesystem</summary>
HAMMER2 = 23,
/// <summary>UDF</summary>
UDF = 24,
/// <summary>System V Boot filesystem</summary>
SysVBoot = 25,
/// <summary>EFS</summary>
EFS = 26,
/// <summary>ZFS</summary>
ZFS = 27,
/// <summary>NiLFS</summary>
NILFS = 27,
/// <summary>Cryptographic disk</summary>
CGD = 28,
/// <summary>MINIX v3</summary>
MINIX = 29,
/// <summary>FreeBSD nandfs</summary>
NANDFS = 30
}
/// <summary>
/// Drive flags
/// </summary>
[Flags]
enum dFlags : uint
{
/// <summary>Removable media</summary>
Removable = 0x01,
/// <summary>Drive supports ECC</summary>
ECC = 0x02,
/// <summary>Drive supports bad sector forwarding</summary>
BadSectorForward = 0x04,
/// <summary>Disk emulator</summary>
RAMDisk = 0x08,
/// <summary>Can do back to back transfer</summary>
Chain = 0x10,
/// <summary>Dynamic geometry device</summary>
DynamicGeometry = 0x20
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct DiskLabel
{
/// <summary>
/// <see cref="DISKMAGIC" />
/// </summary>
public uint d_magic;
/// <summary>
/// <see cref="dType" />
/// </summary>
public dType d_type;
/// <summary>Disk subtype</summary>
public ushort d_subtype;
/// <summary>Type name</summary>
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
public byte[] d_typename;
/// <summary>Pack identifier</summary>
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
public byte[] d_packname;
/// <summary>Bytes per sector</summary>
public uint d_secsize;
/// <summary>Sectors per track</summary>
public uint d_nsectors;
/// <summary>Tracks per cylinder</summary>
public uint d_ntracks;
/// <summary>Cylinders per unit</summary>
public uint d_ncylinders;
/// <summary>Sectors per cylinder</summary>
public uint d_secpercyl;
/// <summary>Sectors per unit</summary>
public uint d_secperunit;
/// <summary>Spare sectors per track</summary>
public ushort d_sparespertrack;
/// <summary>Spare sectors per cylinder</summary>
public ushort d_sparespercyl;
/// <summary>Alternate cylinders</summary>
public uint d_acylinders;
/// <summary>Rotational speed</summary>
public ushort d_rpm;
/// <summary>Hardware sector interleave</summary>
public ushort d_interleave;
/// <summary>Sector 0 skew per track</summary>
public ushort d_trackskew;
/// <summary>Sector 0 sker per cylinder</summary>
public ushort d_cylskeew;
/// <summary>Head switch time in microseconds</summary>
public uint d_headswitch;
/// <summary>Track to track seek in microseconds</summary>
public uint d_trkseek;
/// <summary>
/// <see cref="dFlags" />
/// </summary>
public dFlags d_flags;
/// <summary>Drive-specific information</summary>
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)]
public uint[] d_drivedata;
/// <summary>Reserved</summary>
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)]
public uint[] d_spare;
/// <summary><see cref="DISKMAGIC" /> again</summary>
public uint d_magic2;
/// <summary>XOR of data</summary>
public ushort d_checksum;
/// <summary>How many partitions</summary>
public ushort d_npartitions;
/// <summary>Size of boot area in bytes</summary>
public uint d_bbsize;
/// <summary>Maximum size of superblock in bytes</summary>
public uint d_sbsize;
/// <summary>Partitions</summary>
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 22)]
public BSDPartition[] d_partitions;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct BSDPartition
{
/// <summary>Sectors in partition</summary>
public uint p_size;
/// <summary>Starting sector</summary>
public uint p_offset;
/// <summary>Fragment size</summary>
public uint p_fsize;
/// <summary>Filesystem type, <see cref="fsType" /></summary>
public fsType p_fstype;
/// <summary>Fragment size</summary>
public byte p_frag;
/// <summary>Cylinder per group</summary>
public ushort p_cpg;
}
}
}