From 9a5d7c8dc134d9cc4fb18440b3f3d944e3f01967 Mon Sep 17 00:00:00 2001 From: Natalia Portillo Date: Tue, 18 Feb 2020 23:38:11 +0000 Subject: [PATCH] Split Apple HFS in separate files. --- DiscImageChef.Filesystems/AppleHFS.cs | 451 ------------------ .../AppleHFS/AppleHFS.cs | 50 ++ DiscImageChef.Filesystems/AppleHFS/Consts.cs | 46 ++ DiscImageChef.Filesystems/AppleHFS/Info.cs | 336 +++++++++++++ DiscImageChef.Filesystems/AppleHFS/Structs.cs | 181 +++++++ .../DiscImageChef.Filesystems.csproj | 5 +- 6 files changed, 617 insertions(+), 452 deletions(-) delete mode 100644 DiscImageChef.Filesystems/AppleHFS.cs create mode 100644 DiscImageChef.Filesystems/AppleHFS/AppleHFS.cs create mode 100644 DiscImageChef.Filesystems/AppleHFS/Consts.cs create mode 100644 DiscImageChef.Filesystems/AppleHFS/Info.cs create mode 100644 DiscImageChef.Filesystems/AppleHFS/Structs.cs diff --git a/DiscImageChef.Filesystems/AppleHFS.cs b/DiscImageChef.Filesystems/AppleHFS.cs deleted file mode 100644 index a5eccabec..000000000 --- a/DiscImageChef.Filesystems/AppleHFS.cs +++ /dev/null @@ -1,451 +0,0 @@ -// /*************************************************************************** -// The Disc Image Chef -// ---------------------------------------------------------------------------- -// -// Filename : AppleHFS.cs -// Author(s) : Natalia Portillo -// -// Component : Apple Hierarchical File System plugin. -// -// --[ Description ] ---------------------------------------------------------- -// -// Identifies the Apple Hierarchical File System and shows information. -// -// --[ 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-2020 Natalia Portillo -// ****************************************************************************/ - -using System; -using System.Runtime.InteropServices; -using System.Text; -using DiscImageChef.CommonTypes; -using DiscImageChef.CommonTypes.Interfaces; -using Schemas; -using Marshal = DiscImageChef.Helpers.Marshal; - -namespace DiscImageChef.Filesystems -{ - // Information from Inside Macintosh - // https://developer.apple.com/legacy/library/documentation/mac/pdf/Files/File_Manager.pdf - public class AppleHFS : IFilesystem - { - /// - /// "BD", HFS magic - /// - const ushort HFS_MAGIC = 0x4244; - /// - /// "H+", HFS+ magic - /// - const ushort HFSP_MAGIC = 0x482B; - /// - /// "LK", HFS bootblock magic - /// - const ushort HFSBB_MAGIC = 0x4C4B; - - public FileSystemType XmlFsType { get; private set; } - public Encoding Encoding { get; private set; } - public string Name => "Apple Hierarchical File System"; - public Guid Id => new Guid("36405F8D-0D26-6ECC-0BBB-1D5225FF404F"); - public string Author => "Natalia Portillo"; - - public bool Identify(IMediaImage imagePlugin, Partition partition) - { - if(2 + partition.Start >= partition.End) return false; - - byte[] mdbSector; - ushort drSigWord; - - if(imagePlugin.Info.SectorSize == 2352 || imagePlugin.Info.SectorSize == 2448 || - imagePlugin.Info.SectorSize == 2048) - { - mdbSector = imagePlugin.ReadSectors(partition.Start, 2); - - foreach(int offset in new[] {0, 0x200, 0x400, 0x600, 0x800, 0xA00}) - { - drSigWord = BigEndianBitConverter.ToUInt16(mdbSector, offset); - if(drSigWord != HFS_MAGIC) continue; - - drSigWord = - BigEndianBitConverter.ToUInt16(mdbSector, offset + 0x7C); // Seek to embedded HFS+ signature - - return drSigWord != HFSP_MAGIC; - } - } - else - { - mdbSector = imagePlugin.ReadSector(2 + partition.Start); - drSigWord = BigEndianBitConverter.ToUInt16(mdbSector, 0); - - if(drSigWord != HFS_MAGIC) return false; - - drSigWord = BigEndianBitConverter.ToUInt16(mdbSector, 0x7C); // Seek to embedded HFS+ signature - - return drSigWord != HFSP_MAGIC; - } - - return false; - } - - public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, - Encoding encoding) - { - Encoding = encoding ?? Encoding.GetEncoding("macintosh"); - information = ""; - - StringBuilder sb = new StringBuilder(); - - byte[] bbSector = null; - byte[] mdbSector = null; - ushort drSigWord; - - bool apmFromHddOnCd = false; - - if(imagePlugin.Info.SectorSize == 2352 || imagePlugin.Info.SectorSize == 2448 || - imagePlugin.Info.SectorSize == 2048) - { - byte[] tmpSector = imagePlugin.ReadSectors(partition.Start, 2); - - foreach(int offset in new[] {0, 0x200, 0x400, 0x600, 0x800, 0xA00}) - { - drSigWord = BigEndianBitConverter.ToUInt16(tmpSector, offset); - if(drSigWord != HFS_MAGIC) continue; - - bbSector = new byte[1024]; - mdbSector = new byte[512]; - if(offset >= 0x400) Array.Copy(tmpSector, offset - 0x400, bbSector, 0, 1024); - Array.Copy(tmpSector, offset, mdbSector, 0, 512); - apmFromHddOnCd = true; - break; - } - - if(!apmFromHddOnCd) return; - } - else - { - mdbSector = imagePlugin.ReadSector(2 + partition.Start); - drSigWord = BigEndianBitConverter.ToUInt16(mdbSector, 0); - - if(drSigWord == HFS_MAGIC) bbSector = imagePlugin.ReadSector(partition.Start); - else return; - } - - HfsMasterDirectoryBlock mdb = Marshal.ByteArrayToStructureBigEndian(mdbSector); - HfsBootBlock bb = Marshal.ByteArrayToStructureBigEndian(bbSector); - - sb.AppendLine("Apple Hierarchical File System"); - sb.AppendLine(); - if(apmFromHddOnCd) - sb.AppendLine("HFS uses 512 bytes/sector while device uses 2048 bytes/sector.").AppendLine(); - sb.AppendLine("Master Directory Block:"); - sb.AppendFormat("Creation date: {0}", DateHandlers.MacToDateTime(mdb.drCrDate)).AppendLine(); - sb.AppendFormat("Last modification date: {0}", DateHandlers.MacToDateTime(mdb.drLsMod)).AppendLine(); - if(mdb.drVolBkUp > 0) - { - sb.AppendFormat("Last backup date: {0}", DateHandlers.MacToDateTime(mdb.drVolBkUp)).AppendLine(); - sb.AppendFormat("Backup sequence number: {0}", mdb.drVSeqNum).AppendLine(); - } - else sb.AppendLine("Volume has never been backed up"); - - if((mdb.drAtrb & 0x80) == 0x80) sb.AppendLine("Volume is locked by hardware."); - sb.AppendLine((mdb.drAtrb & 0x100) == 0x100 ? "Volume was unmonted." : "Volume is mounted."); - if((mdb.drAtrb & 0x200) == 0x200) sb.AppendLine("Volume has spared bad blocks."); - if((mdb.drAtrb & 0x400) == 0x400) sb.AppendLine("Volume does not need cache."); - if((mdb.drAtrb & 0x800) == 0x800) sb.AppendLine("Boot volume is inconsistent."); - if((mdb.drAtrb & 0x1000) == 0x1000) sb.AppendLine("There are reused CNIDs."); - if((mdb.drAtrb & 0x2000) == 0x2000) sb.AppendLine("Volume is journaled."); - if((mdb.drAtrb & 0x4000) == 0x4000) sb.AppendLine("Volume is seriously inconsistent."); - if((mdb.drAtrb & 0x8000) == 0x8000) sb.AppendLine("Volume is locked by software."); - - sb.AppendFormat("{0} files on root directory", mdb.drNmFls).AppendLine(); - sb.AppendFormat("{0} directories on root directory", mdb.drNmRtDirs).AppendLine(); - sb.AppendFormat("{0} files on volume", mdb.drFilCnt).AppendLine(); - sb.AppendFormat("{0} directories on volume", mdb.drDirCnt).AppendLine(); - sb.AppendFormat("Volume write count: {0}", mdb.drWrCnt).AppendLine(); - - sb.AppendFormat("Volume bitmap starting sector (in 512-bytes): {0}", mdb.drVBMSt).AppendLine(); - sb.AppendFormat("Next allocation block: {0}.", mdb.drAllocPtr).AppendLine(); - sb.AppendFormat("{0} volume allocation blocks.", mdb.drNmAlBlks).AppendLine(); - sb.AppendFormat("{0} bytes per allocation block.", mdb.drAlBlkSiz).AppendLine(); - sb.AppendFormat("{0} bytes to allocate when extending a file.", mdb.drClpSiz).AppendLine(); - sb.AppendFormat("{0} bytes to allocate when extending a Extents B-Tree.", mdb.drXTClpSiz).AppendLine(); - sb.AppendFormat("{0} bytes to allocate when extending a Catalog B-Tree.", mdb.drCTClpSiz).AppendLine(); - sb.AppendFormat("Sector of first allocation block: {0}", mdb.drAlBlSt).AppendLine(); - sb.AppendFormat("Next unused CNID: {0}", mdb.drNxtCNID).AppendLine(); - sb.AppendFormat("{0} unused allocation blocks.", mdb.drFreeBks).AppendLine(); - - sb.AppendFormat("{0} bytes in the Extents B-Tree", mdb.drXTFlSize).AppendLine(); - sb.AppendFormat("{0} bytes in the Catalog B-Tree", mdb.drCTFlSize).AppendLine(); - - sb.AppendFormat("Volume name: {0}", StringHandlers.PascalToString(mdb.drVN, Encoding)).AppendLine(); - - sb.AppendLine("Finder info:"); - sb.AppendFormat("CNID of bootable system's directory: {0}", mdb.drFndrInfo0).AppendLine(); - sb.AppendFormat("CNID of first-run application's directory: {0}", mdb.drFndrInfo1).AppendLine(); - sb.AppendFormat("CNID of previously opened directory: {0}", mdb.drFndrInfo2).AppendLine(); - sb.AppendFormat("CNID of bootable Mac OS 8 or 9 directory: {0}", mdb.drFndrInfo3).AppendLine(); - sb.AppendFormat("CNID of bootable Mac OS X directory: {0}", mdb.drFndrInfo5).AppendLine(); - if(mdb.drFndrInfo6 != 0 && mdb.drFndrInfo7 != 0) - sb.AppendFormat("Mac OS X Volume ID: {0:X8}{1:X8}", mdb.drFndrInfo6, mdb.drFndrInfo7).AppendLine(); - - if(mdb.drEmbedSigWord == HFSP_MAGIC) - { - sb.AppendLine("Volume wraps a HFS+ volume."); - sb.AppendFormat("Starting block of the HFS+ volume: {0}", mdb.xdrStABNt).AppendLine(); - sb.AppendFormat("Allocations blocks of the HFS+ volume: {0}", mdb.xdrNumABlks).AppendLine(); - } - else - { - sb.AppendFormat("{0} blocks in volume cache", mdb.drVCSize).AppendLine(); - sb.AppendFormat("{0} blocks in volume bitmap cache", mdb.drVBMCSize).AppendLine(); - sb.AppendFormat("{0} blocks in volume common cache", mdb.drCtlCSize).AppendLine(); - } - - if(bb.signature == HFSBB_MAGIC) - { - sb.AppendLine("Volume is bootable."); - sb.AppendLine(); - sb.AppendLine("Boot Block:"); - if((bb.boot_flags & 0x40) == 0x40) sb.AppendLine("Boot block should be executed."); - if((bb.boot_flags & 0x80) == 0x80) sb.AppendLine("Boot block is in new unknown format."); - else - { - if(bb.boot_flags > 0) sb.AppendLine("Allocate secondary sound buffer at boot."); - else if(bb.boot_flags < 0) sb.AppendLine("Allocate secondary sound and video buffers at boot."); - - sb.AppendFormat("System filename: {0}", StringHandlers.PascalToString(bb.system_name, Encoding)) - .AppendLine(); - sb.AppendFormat("Finder filename: {0}", StringHandlers.PascalToString(bb.finder_name, Encoding)) - .AppendLine(); - sb.AppendFormat("Debugger filename: {0}", StringHandlers.PascalToString(bb.debug_name, Encoding)) - .AppendLine(); - sb.AppendFormat("Disassembler filename: {0}", - StringHandlers.PascalToString(bb.disasm_name, Encoding)).AppendLine(); - sb.AppendFormat("Startup screen filename: {0}", - StringHandlers.PascalToString(bb.stupscr_name, Encoding)).AppendLine(); - sb.AppendFormat("First program to execute at boot: {0}", - StringHandlers.PascalToString(bb.bootup_name, Encoding)).AppendLine(); - sb.AppendFormat("Clipboard filename: {0}", StringHandlers.PascalToString(bb.clipbrd_name, Encoding)) - .AppendLine(); - sb.AppendFormat("Maximum opened files: {0}", bb.max_files * 4).AppendLine(); - sb.AppendFormat("Event queue size: {0}", bb.queue_size).AppendLine(); - sb.AppendFormat("Heap size with 128KiB of RAM: {0} bytes", bb.heap_128k).AppendLine(); - sb.AppendFormat("Heap size with 256KiB of RAM: {0} bytes", bb.heap_256k).AppendLine(); - sb.AppendFormat("Heap size with 512KiB of RAM or more: {0} bytes", bb.heap_512k).AppendLine(); - } - } - else if(mdb.drFndrInfo0 != 0 || mdb.drFndrInfo3 != 0 || mdb.drFndrInfo5 != 0) - sb.AppendLine("Volume is bootable."); - else sb.AppendLine("Volume is not bootable."); - - information = sb.ToString(); - - XmlFsType = new FileSystemType(); - if(mdb.drVolBkUp > 0) - { - XmlFsType.BackupDate = DateHandlers.MacToDateTime(mdb.drVolBkUp); - XmlFsType.BackupDateSpecified = true; - } - - XmlFsType.Bootable = bb.signature == HFSBB_MAGIC || mdb.drFndrInfo0 != 0 || mdb.drFndrInfo3 != 0 || - mdb.drFndrInfo5 != 0; - XmlFsType.Clusters = mdb.drNmAlBlks; - XmlFsType.ClusterSize = mdb.drAlBlkSiz; - if(mdb.drCrDate > 0) - { - XmlFsType.CreationDate = DateHandlers.MacToDateTime(mdb.drCrDate); - XmlFsType.CreationDateSpecified = true; - } - - XmlFsType.Dirty = (mdb.drAtrb & 0x100) != 0x100; - XmlFsType.Files = mdb.drFilCnt; - XmlFsType.FilesSpecified = true; - XmlFsType.FreeClusters = mdb.drFreeBks; - XmlFsType.FreeClustersSpecified = true; - if(mdb.drLsMod > 0) - { - XmlFsType.ModificationDate = DateHandlers.MacToDateTime(mdb.drLsMod); - XmlFsType.ModificationDateSpecified = true; - } - - XmlFsType.Type = "HFS"; - XmlFsType.VolumeName = StringHandlers.PascalToString(mdb.drVN, Encoding); - if(mdb.drFndrInfo6 != 0 && mdb.drFndrInfo7 != 0) - XmlFsType.VolumeSerial = $"{mdb.drFndrInfo6:X8}{mdb.drFndrInfo7:X8}"; - } - - static byte[] Read2048SectorAs512(IMediaImage imagePlugin, ulong lba) - { - ulong lba2K = lba / 4; - int remainder = (int)(lba % 4); - - byte[] buffer = imagePlugin.ReadSector(lba2K); - byte[] sector = new byte[512]; - - Array.Copy(buffer, remainder * 512, sector, 0, 512); - - return sector; - } - - /// - /// Master Directory Block, should be sector 2 in volume - /// - [StructLayout(LayoutKind.Sequential, Pack = 1)] - struct HfsMasterDirectoryBlock // Should be sector 2 in volume - { - /// 0x000, Signature, 0x4244 - public readonly ushort drSigWord; - /// 0x002, Volume creation date - public readonly uint drCrDate; - /// 0x006, Volume last modification date - public readonly uint drLsMod; - /// 0x00A, Volume attributes - public readonly ushort drAtrb; - /// 0x00C, Files in root directory - public readonly ushort drNmFls; - /// 0x00E, Start 512-byte sector of volume bitmap - public readonly ushort drVBMSt; - /// 0x010, Allocation block to begin next allocation - public readonly ushort drAllocPtr; - /// 0x012, Allocation blocks - public readonly ushort drNmAlBlks; - /// 0x014, Bytes per allocation block - public readonly uint drAlBlkSiz; - /// 0x018, Bytes to allocate when extending a file - public readonly uint drClpSiz; - /// 0x01C, Start 512-byte sector of first allocation block - public readonly ushort drAlBlSt; - /// 0x01E, CNID for next file - public readonly uint drNxtCNID; - /// 0x022, Free allocation blocks - public readonly ushort drFreeBks; - /// 0x024, Volume name (28 bytes) - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 28)] - public readonly byte[] drVN; - /// 0x040, Volume last backup time - public readonly uint drVolBkUp; - /// 0x044, Volume backup sequence number - public readonly ushort drVSeqNum; - /// 0x046, Filesystem write count - public readonly uint drWrCnt; - /// 0x04A, Bytes to allocate when extending the extents B-Tree - public readonly uint drXTClpSiz; - /// 0x04E, Bytes to allocate when extending the catalog B-Tree - public readonly uint drCTClpSiz; - /// 0x052, Number of directories in root directory - public readonly ushort drNmRtDirs; - /// 0x054, Number of files in the volume - public readonly uint drFilCnt; - /// 0x058, Number of directories in the volume - public readonly uint drDirCnt; - /// 0x05C, finderInfo[0], CNID for bootable system's directory - public readonly uint drFndrInfo0; - /// 0x060, finderInfo[1], CNID of the directory containing the boot application - public readonly uint drFndrInfo1; - /// 0x064, finderInfo[2], CNID of the directory that should be opened on boot - public readonly uint drFndrInfo2; - /// 0x068, finderInfo[3], CNID for Mac OS 8 or 9 directory - public readonly uint drFndrInfo3; - /// 0x06C, finderInfo[4], Reserved - public readonly uint drFndrInfo4; - /// 0x070, finderInfo[5], CNID for Mac OS X directory - public readonly uint drFndrInfo5; - /// 0x074, finderInfo[6], first part of Mac OS X volume ID - public readonly uint drFndrInfo6; - /// 0x078, finderInfo[7], second part of Mac OS X volume ID - public readonly uint drFndrInfo7; - // If wrapping HFS+ - /// 0x07C, Embedded volume signature, "H+" if HFS+ is embedded ignore following two fields if not - public readonly ushort drEmbedSigWord; - /// 0x07E, Starting block number of embedded HFS+ volume - public readonly ushort xdrStABNt; - /// 0x080, Allocation blocks used by embedded volume - public readonly ushort xdrNumABlks; - // If not - /// 0x07C, Size in blocks of volume cache - public readonly ushort drVCSize; - /// 0x07E, Size in blocks of volume bitmap cache - public readonly ushort drVBMCSize; - /// 0x080, Size in blocks of volume common cache - public readonly ushort drCtlCSize; - // End of variable variables :D - /// - /// 0x082, Bytes in the extents B-Tree - /// 3 HFS extents following, 32 bits each - /// - public readonly uint drXTFlSize; - /// - /// 0x092, Bytes in the catalog B-Tree - /// 3 HFS extents following, 32 bits each - /// - public readonly uint drCTFlSize; - } - - /// - /// Should be sectors 0 and 1 in volume, followed by boot code - /// - [StructLayout(LayoutKind.Sequential, Pack = 1)] - struct HfsBootBlock // Should be sectors 0 and 1 in volume - { - /// 0x000, Signature, 0x4C4B if bootable - public readonly ushort signature; - /// 0x002, Branch - public readonly uint branch; - /// 0x007, Boot block version - public readonly ushort boot_version; - /// 0x006, Boot block flags - public readonly short boot_flags; - /// 0x00A, System file name (16 bytes) - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] - public readonly byte[] system_name; - /// 0x01A, Finder file name (16 bytes) - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] - public readonly byte[] finder_name; - /// 0x02A, Debugger file name (16 bytes) - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] - public readonly byte[] debug_name; - /// 0x03A, Disassembler file name (16 bytes) - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] - public readonly byte[] disasm_name; - /// 0x04A, Startup screen file name (16 bytes) - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] - public readonly byte[] stupscr_name; - /// 0x05A, First program to execute on boot (16 bytes) - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] - public readonly byte[] bootup_name; - /// 0x06A, Clipboard file name (16 bytes) - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] - public readonly byte[] clipbrd_name; - /// 0x07A, 1/4 of maximum opened at a time files - public readonly ushort max_files; - /// 0x07C, Event queue size - public readonly ushort queue_size; - /// 0x07E, Heap size on a Mac with 128KiB of RAM - public readonly uint heap_128k; - /// 0x082, Heap size on a Mac with 256KiB of RAM - public readonly uint heap_256k; - /// 0x086, Heap size on a Mac with 512KiB of RAM or more - public readonly uint heap_512k; - /// Padding - public readonly ushort padding; - /// Additional system heap space - public readonly uint heap_extra; - /// Fraction of RAM for system heap - public readonly uint heap_fract; - } - } -} \ No newline at end of file diff --git a/DiscImageChef.Filesystems/AppleHFS/AppleHFS.cs b/DiscImageChef.Filesystems/AppleHFS/AppleHFS.cs new file mode 100644 index 000000000..c695358cf --- /dev/null +++ b/DiscImageChef.Filesystems/AppleHFS/AppleHFS.cs @@ -0,0 +1,50 @@ +// /*************************************************************************** +// The Disc Image Chef +// ---------------------------------------------------------------------------- +// +// Filename : AppleHFS.cs +// Author(s) : Natalia Portillo +// +// Component : Apple Hierarchical File System plugin. +// +// --[ Description ] ---------------------------------------------------------- +// +// Identifies the Apple Hierarchical File System and shows information. +// +// --[ 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-2020 Natalia Portillo +// ****************************************************************************/ + +using System; +using System.Text; +using DiscImageChef.CommonTypes.Interfaces; +using Schemas; + +namespace DiscImageChef.Filesystems +{ + // Information from Inside Macintosh + // https://developer.apple.com/legacy/library/documentation/mac/pdf/Files/File_Manager.pdf + public partial class AppleHFS : IFilesystem + { + public FileSystemType XmlFsType { get; private set; } + public Encoding Encoding { get; private set; } + public string Name => "Apple Hierarchical File System"; + public Guid Id => new Guid("36405F8D-0D26-6ECC-0BBB-1D5225FF404F"); + public string Author => "Natalia Portillo"; + } +} \ No newline at end of file diff --git a/DiscImageChef.Filesystems/AppleHFS/Consts.cs b/DiscImageChef.Filesystems/AppleHFS/Consts.cs new file mode 100644 index 000000000..028e7cc64 --- /dev/null +++ b/DiscImageChef.Filesystems/AppleHFS/Consts.cs @@ -0,0 +1,46 @@ +// /*************************************************************************** +// The Disc Image Chef +// ---------------------------------------------------------------------------- +// +// Filename : AppleHFS.cs +// Author(s) : Natalia Portillo +// +// Component : Apple Hierarchical File System plugin. +// +// --[ Description ] ---------------------------------------------------------- +// +// Identifies the Apple Hierarchical File System and shows information. +// +// --[ 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-2020 Natalia Portillo +// ****************************************************************************/ + +namespace DiscImageChef.Filesystems +{ + // Information from Inside Macintosh + // https://developer.apple.com/legacy/library/documentation/mac/pdf/Files/File_Manager.pdf + public partial class AppleHFS + { + /// "BD", HFS magic + const ushort HFS_MAGIC = 0x4244; + /// "H+", HFS+ magic + const ushort HFSP_MAGIC = 0x482B; + /// "LK", HFS bootblock magic + const ushort HFSBB_MAGIC = 0x4C4B; + } +} \ No newline at end of file diff --git a/DiscImageChef.Filesystems/AppleHFS/Info.cs b/DiscImageChef.Filesystems/AppleHFS/Info.cs new file mode 100644 index 000000000..a25fbb631 --- /dev/null +++ b/DiscImageChef.Filesystems/AppleHFS/Info.cs @@ -0,0 +1,336 @@ +// /*************************************************************************** +// The Disc Image Chef +// ---------------------------------------------------------------------------- +// +// Filename : AppleHFS.cs +// Author(s) : Natalia Portillo +// +// Component : Apple Hierarchical File System plugin. +// +// --[ Description ] ---------------------------------------------------------- +// +// Identifies the Apple Hierarchical File System and shows information. +// +// --[ 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-2020 Natalia Portillo +// ****************************************************************************/ + +using System; +using System.Text; +using DiscImageChef.CommonTypes; +using DiscImageChef.CommonTypes.Interfaces; +using DiscImageChef.Helpers; +using Schemas; + +namespace DiscImageChef.Filesystems +{ + // Information from Inside Macintosh + // https://developer.apple.com/legacy/library/documentation/mac/pdf/Files/File_Manager.pdf + public partial class AppleHFS + { + public bool Identify(IMediaImage imagePlugin, Partition partition) + { + if(2 + partition.Start >= partition.End) + return false; + + byte[] mdbSector; + ushort drSigWord; + + if(imagePlugin.Info.SectorSize == 2352 || + imagePlugin.Info.SectorSize == 2448 || + imagePlugin.Info.SectorSize == 2048) + { + mdbSector = imagePlugin.ReadSectors(partition.Start, 2); + + foreach(int offset in new[] + { + 0, 0x200, 0x400, 0x600, 0x800, 0xA00 + }) + { + drSigWord = BigEndianBitConverter.ToUInt16(mdbSector, offset); + + if(drSigWord != HFS_MAGIC) + continue; + + drSigWord = + BigEndianBitConverter.ToUInt16(mdbSector, offset + 0x7C); // Seek to embedded HFS+ signature + + return drSigWord != HFSP_MAGIC; + } + } + else + { + mdbSector = imagePlugin.ReadSector(2 + partition.Start); + drSigWord = BigEndianBitConverter.ToUInt16(mdbSector, 0); + + if(drSigWord != HFS_MAGIC) + return false; + + drSigWord = BigEndianBitConverter.ToUInt16(mdbSector, 0x7C); // Seek to embedded HFS+ signature + + return drSigWord != HFSP_MAGIC; + } + + return false; + } + + public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, + Encoding encoding) + { + Encoding = encoding ?? Encoding.GetEncoding("macintosh"); + information = ""; + + var sb = new StringBuilder(); + + byte[] bbSector = null; + byte[] mdbSector = null; + ushort drSigWord; + + bool apmFromHddOnCd = false; + + if(imagePlugin.Info.SectorSize == 2352 || + imagePlugin.Info.SectorSize == 2448 || + imagePlugin.Info.SectorSize == 2048) + { + byte[] tmpSector = imagePlugin.ReadSectors(partition.Start, 2); + + foreach(int offset in new[] + { + 0, 0x200, 0x400, 0x600, 0x800, 0xA00 + }) + { + drSigWord = BigEndianBitConverter.ToUInt16(tmpSector, offset); + + if(drSigWord != HFS_MAGIC) + continue; + + bbSector = new byte[1024]; + mdbSector = new byte[512]; + + if(offset >= 0x400) + Array.Copy(tmpSector, offset - 0x400, bbSector, 0, 1024); + + Array.Copy(tmpSector, offset, mdbSector, 0, 512); + apmFromHddOnCd = true; + + break; + } + + if(!apmFromHddOnCd) + return; + } + else + { + mdbSector = imagePlugin.ReadSector(2 + partition.Start); + drSigWord = BigEndianBitConverter.ToUInt16(mdbSector, 0); + + if(drSigWord == HFS_MAGIC) + bbSector = imagePlugin.ReadSector(partition.Start); + else + return; + } + + HfsMasterDirectoryBlock mdb = Marshal.ByteArrayToStructureBigEndian(mdbSector); + HfsBootBlock bb = Marshal.ByteArrayToStructureBigEndian(bbSector); + + sb.AppendLine("Apple Hierarchical File System"); + sb.AppendLine(); + + if(apmFromHddOnCd) + sb.AppendLine("HFS uses 512 bytes/sector while device uses 2048 bytes/sector.").AppendLine(); + + sb.AppendLine("Master Directory Block:"); + sb.AppendFormat("Creation date: {0}", DateHandlers.MacToDateTime(mdb.drCrDate)).AppendLine(); + sb.AppendFormat("Last modification date: {0}", DateHandlers.MacToDateTime(mdb.drLsMod)).AppendLine(); + + if(mdb.drVolBkUp > 0) + { + sb.AppendFormat("Last backup date: {0}", DateHandlers.MacToDateTime(mdb.drVolBkUp)).AppendLine(); + sb.AppendFormat("Backup sequence number: {0}", mdb.drVSeqNum).AppendLine(); + } + else + sb.AppendLine("Volume has never been backed up"); + + if((mdb.drAtrb & 0x80) == 0x80) + sb.AppendLine("Volume is locked by hardware."); + + sb.AppendLine((mdb.drAtrb & 0x100) == 0x100 ? "Volume was unmonted." : "Volume is mounted."); + + if((mdb.drAtrb & 0x200) == 0x200) + sb.AppendLine("Volume has spared bad blocks."); + + if((mdb.drAtrb & 0x400) == 0x400) + sb.AppendLine("Volume does not need cache."); + + if((mdb.drAtrb & 0x800) == 0x800) + sb.AppendLine("Boot volume is inconsistent."); + + if((mdb.drAtrb & 0x1000) == 0x1000) + sb.AppendLine("There are reused CNIDs."); + + if((mdb.drAtrb & 0x2000) == 0x2000) + sb.AppendLine("Volume is journaled."); + + if((mdb.drAtrb & 0x4000) == 0x4000) + sb.AppendLine("Volume is seriously inconsistent."); + + if((mdb.drAtrb & 0x8000) == 0x8000) + sb.AppendLine("Volume is locked by software."); + + sb.AppendFormat("{0} files on root directory", mdb.drNmFls).AppendLine(); + sb.AppendFormat("{0} directories on root directory", mdb.drNmRtDirs).AppendLine(); + sb.AppendFormat("{0} files on volume", mdb.drFilCnt).AppendLine(); + sb.AppendFormat("{0} directories on volume", mdb.drDirCnt).AppendLine(); + sb.AppendFormat("Volume write count: {0}", mdb.drWrCnt).AppendLine(); + + sb.AppendFormat("Volume bitmap starting sector (in 512-bytes): {0}", mdb.drVBMSt).AppendLine(); + sb.AppendFormat("Next allocation block: {0}.", mdb.drAllocPtr).AppendLine(); + sb.AppendFormat("{0} volume allocation blocks.", mdb.drNmAlBlks).AppendLine(); + sb.AppendFormat("{0} bytes per allocation block.", mdb.drAlBlkSiz).AppendLine(); + sb.AppendFormat("{0} bytes to allocate when extending a file.", mdb.drClpSiz).AppendLine(); + sb.AppendFormat("{0} bytes to allocate when extending a Extents B-Tree.", mdb.drXTClpSiz).AppendLine(); + sb.AppendFormat("{0} bytes to allocate when extending a Catalog B-Tree.", mdb.drCTClpSiz).AppendLine(); + sb.AppendFormat("Sector of first allocation block: {0}", mdb.drAlBlSt).AppendLine(); + sb.AppendFormat("Next unused CNID: {0}", mdb.drNxtCNID).AppendLine(); + sb.AppendFormat("{0} unused allocation blocks.", mdb.drFreeBks).AppendLine(); + + sb.AppendFormat("{0} bytes in the Extents B-Tree", mdb.drXTFlSize).AppendLine(); + sb.AppendFormat("{0} bytes in the Catalog B-Tree", mdb.drCTFlSize).AppendLine(); + + sb.AppendFormat("Volume name: {0}", StringHandlers.PascalToString(mdb.drVN, Encoding)).AppendLine(); + + sb.AppendLine("Finder info:"); + sb.AppendFormat("CNID of bootable system's directory: {0}", mdb.drFndrInfo0).AppendLine(); + sb.AppendFormat("CNID of first-run application's directory: {0}", mdb.drFndrInfo1).AppendLine(); + sb.AppendFormat("CNID of previously opened directory: {0}", mdb.drFndrInfo2).AppendLine(); + sb.AppendFormat("CNID of bootable Mac OS 8 or 9 directory: {0}", mdb.drFndrInfo3).AppendLine(); + sb.AppendFormat("CNID of bootable Mac OS X directory: {0}", mdb.drFndrInfo5).AppendLine(); + + if(mdb.drFndrInfo6 != 0 && + mdb.drFndrInfo7 != 0) + sb.AppendFormat("Mac OS X Volume ID: {0:X8}{1:X8}", mdb.drFndrInfo6, mdb.drFndrInfo7).AppendLine(); + + if(mdb.drEmbedSigWord == HFSP_MAGIC) + { + sb.AppendLine("Volume wraps a HFS+ volume."); + sb.AppendFormat("Starting block of the HFS+ volume: {0}", mdb.xdrStABNt).AppendLine(); + sb.AppendFormat("Allocations blocks of the HFS+ volume: {0}", mdb.xdrNumABlks).AppendLine(); + } + else + { + sb.AppendFormat("{0} blocks in volume cache", mdb.drVCSize).AppendLine(); + sb.AppendFormat("{0} blocks in volume bitmap cache", mdb.drVBMCSize).AppendLine(); + sb.AppendFormat("{0} blocks in volume common cache", mdb.drCtlCSize).AppendLine(); + } + + if(bb.signature == HFSBB_MAGIC) + { + sb.AppendLine("Volume is bootable."); + sb.AppendLine(); + sb.AppendLine("Boot Block:"); + + if((bb.boot_flags & 0x40) == 0x40) + sb.AppendLine("Boot block should be executed."); + + if((bb.boot_flags & 0x80) == 0x80) + sb.AppendLine("Boot block is in new unknown format."); + else + { + if(bb.boot_flags > 0) + sb.AppendLine("Allocate secondary sound buffer at boot."); + else if(bb.boot_flags < 0) + sb.AppendLine("Allocate secondary sound and video buffers at boot."); + + sb.AppendFormat("System filename: {0}", StringHandlers.PascalToString(bb.system_name, Encoding)). + AppendLine(); + + sb.AppendFormat("Finder filename: {0}", StringHandlers.PascalToString(bb.finder_name, Encoding)). + AppendLine(); + + sb.AppendFormat("Debugger filename: {0}", StringHandlers.PascalToString(bb.debug_name, Encoding)). + AppendLine(); + + sb.AppendFormat("Disassembler filename: {0}", + StringHandlers.PascalToString(bb.disasm_name, Encoding)).AppendLine(); + + sb.AppendFormat("Startup screen filename: {0}", + StringHandlers.PascalToString(bb.stupscr_name, Encoding)).AppendLine(); + + sb.AppendFormat("First program to execute at boot: {0}", + StringHandlers.PascalToString(bb.bootup_name, Encoding)).AppendLine(); + + sb.AppendFormat("Clipboard filename: {0}", + StringHandlers.PascalToString(bb.clipbrd_name, Encoding)).AppendLine(); + + sb.AppendFormat("Maximum opened files: {0}", bb.max_files * 4).AppendLine(); + sb.AppendFormat("Event queue size: {0}", bb.queue_size).AppendLine(); + sb.AppendFormat("Heap size with 128KiB of RAM: {0} bytes", bb.heap_128k).AppendLine(); + sb.AppendFormat("Heap size with 256KiB of RAM: {0} bytes", bb.heap_256k).AppendLine(); + sb.AppendFormat("Heap size with 512KiB of RAM or more: {0} bytes", bb.heap_512k).AppendLine(); + } + } + else if(mdb.drFndrInfo0 != 0 || + mdb.drFndrInfo3 != 0 || + mdb.drFndrInfo5 != 0) + sb.AppendLine("Volume is bootable."); + else + sb.AppendLine("Volume is not bootable."); + + information = sb.ToString(); + + XmlFsType = new FileSystemType(); + + if(mdb.drVolBkUp > 0) + { + XmlFsType.BackupDate = DateHandlers.MacToDateTime(mdb.drVolBkUp); + XmlFsType.BackupDateSpecified = true; + } + + XmlFsType.Bootable = bb.signature == HFSBB_MAGIC || mdb.drFndrInfo0 != 0 || mdb.drFndrInfo3 != 0 || + mdb.drFndrInfo5 != 0; + + XmlFsType.Clusters = mdb.drNmAlBlks; + XmlFsType.ClusterSize = mdb.drAlBlkSiz; + + if(mdb.drCrDate > 0) + { + XmlFsType.CreationDate = DateHandlers.MacToDateTime(mdb.drCrDate); + XmlFsType.CreationDateSpecified = true; + } + + XmlFsType.Dirty = (mdb.drAtrb & 0x100) != 0x100; + XmlFsType.Files = mdb.drFilCnt; + XmlFsType.FilesSpecified = true; + XmlFsType.FreeClusters = mdb.drFreeBks; + XmlFsType.FreeClustersSpecified = true; + + if(mdb.drLsMod > 0) + { + XmlFsType.ModificationDate = DateHandlers.MacToDateTime(mdb.drLsMod); + XmlFsType.ModificationDateSpecified = true; + } + + XmlFsType.Type = "HFS"; + XmlFsType.VolumeName = StringHandlers.PascalToString(mdb.drVN, Encoding); + + if(mdb.drFndrInfo6 != 0 && + mdb.drFndrInfo7 != 0) + XmlFsType.VolumeSerial = $"{mdb.drFndrInfo6:X8}{mdb.drFndrInfo7:X8}"; + } + } +} \ No newline at end of file diff --git a/DiscImageChef.Filesystems/AppleHFS/Structs.cs b/DiscImageChef.Filesystems/AppleHFS/Structs.cs new file mode 100644 index 000000000..75344700f --- /dev/null +++ b/DiscImageChef.Filesystems/AppleHFS/Structs.cs @@ -0,0 +1,181 @@ +// /*************************************************************************** +// The Disc Image Chef +// ---------------------------------------------------------------------------- +// +// Filename : AppleHFS.cs +// Author(s) : Natalia Portillo +// +// Component : Apple Hierarchical File System plugin. +// +// --[ Description ] ---------------------------------------------------------- +// +// Identifies the Apple Hierarchical File System and shows information. +// +// --[ 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-2020 Natalia Portillo +// ****************************************************************************/ + +using System.Runtime.InteropServices; + +namespace DiscImageChef.Filesystems +{ + // Information from Inside Macintosh + // https://developer.apple.com/legacy/library/documentation/mac/pdf/Files/File_Manager.pdf + public partial class AppleHFS + { + /// Should be sectors 0 and 1 in volume, followed by boot code + [StructLayout(LayoutKind.Sequential, Pack = 1)] + struct HfsBootBlock // Should be sectors 0 and 1 in volume + { + /// 0x000, Signature, 0x4C4B if bootable + public readonly ushort signature; + /// 0x002, Branch + public readonly uint branch; + /// 0x007, Boot block version + public readonly ushort boot_version; + /// 0x006, Boot block flags + public readonly short boot_flags; + /// 0x00A, System file name (16 bytes) + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] + public readonly byte[] system_name; + /// 0x01A, Finder file name (16 bytes) + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] + public readonly byte[] finder_name; + /// 0x02A, Debugger file name (16 bytes) + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] + public readonly byte[] debug_name; + /// 0x03A, Disassembler file name (16 bytes) + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] + public readonly byte[] disasm_name; + /// 0x04A, Startup screen file name (16 bytes) + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] + public readonly byte[] stupscr_name; + /// 0x05A, First program to execute on boot (16 bytes) + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] + public readonly byte[] bootup_name; + /// 0x06A, Clipboard file name (16 bytes) + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] + public readonly byte[] clipbrd_name; + /// 0x07A, 1/4 of maximum opened at a time files + public readonly ushort max_files; + /// 0x07C, Event queue size + public readonly ushort queue_size; + /// 0x07E, Heap size on a Mac with 128KiB of RAM + public readonly uint heap_128k; + /// 0x082, Heap size on a Mac with 256KiB of RAM + public readonly uint heap_256k; + /// 0x086, Heap size on a Mac with 512KiB of RAM or more + public readonly uint heap_512k; + /// Padding + public readonly ushort padding; + /// Additional system heap space + public readonly uint heap_extra; + /// Fraction of RAM for system heap + public readonly uint heap_fract; + } + + /// Master Directory Block, should be sector 2 in volume + [StructLayout(LayoutKind.Sequential, Pack = 1)] + struct HfsMasterDirectoryBlock // Should be sector 2 in volume + { + /// 0x000, Signature, 0x4244 + public readonly ushort drSigWord; + /// 0x002, Volume creation date + public readonly uint drCrDate; + /// 0x006, Volume last modification date + public readonly uint drLsMod; + /// 0x00A, Volume attributes + public readonly ushort drAtrb; + /// 0x00C, Files in root directory + public readonly ushort drNmFls; + /// 0x00E, Start 512-byte sector of volume bitmap + public readonly ushort drVBMSt; + /// 0x010, Allocation block to begin next allocation + public readonly ushort drAllocPtr; + /// 0x012, Allocation blocks + public readonly ushort drNmAlBlks; + /// 0x014, Bytes per allocation block + public readonly uint drAlBlkSiz; + /// 0x018, Bytes to allocate when extending a file + public readonly uint drClpSiz; + /// 0x01C, Start 512-byte sector of first allocation block + public readonly ushort drAlBlSt; + /// 0x01E, CNID for next file + public readonly uint drNxtCNID; + /// 0x022, Free allocation blocks + public readonly ushort drFreeBks; + /// 0x024, Volume name (28 bytes) + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 28)] + public readonly byte[] drVN; + /// 0x040, Volume last backup time + public readonly uint drVolBkUp; + /// 0x044, Volume backup sequence number + public readonly ushort drVSeqNum; + /// 0x046, Filesystem write count + public readonly uint drWrCnt; + /// 0x04A, Bytes to allocate when extending the extents B-Tree + public readonly uint drXTClpSiz; + /// 0x04E, Bytes to allocate when extending the catalog B-Tree + public readonly uint drCTClpSiz; + /// 0x052, Number of directories in root directory + public readonly ushort drNmRtDirs; + /// 0x054, Number of files in the volume + public readonly uint drFilCnt; + /// 0x058, Number of directories in the volume + public readonly uint drDirCnt; + /// 0x05C, finderInfo[0], CNID for bootable system's directory + public readonly uint drFndrInfo0; + /// 0x060, finderInfo[1], CNID of the directory containing the boot application + public readonly uint drFndrInfo1; + /// 0x064, finderInfo[2], CNID of the directory that should be opened on boot + public readonly uint drFndrInfo2; + /// 0x068, finderInfo[3], CNID for Mac OS 8 or 9 directory + public readonly uint drFndrInfo3; + /// 0x06C, finderInfo[4], Reserved + public readonly uint drFndrInfo4; + /// 0x070, finderInfo[5], CNID for Mac OS X directory + public readonly uint drFndrInfo5; + /// 0x074, finderInfo[6], first part of Mac OS X volume ID + public readonly uint drFndrInfo6; + /// 0x078, finderInfo[7], second part of Mac OS X volume ID + public readonly uint drFndrInfo7; + + // If wrapping HFS+ + /// 0x07C, Embedded volume signature, "H+" if HFS+ is embedded ignore following two fields if not + public readonly ushort drEmbedSigWord; + /// 0x07E, Starting block number of embedded HFS+ volume + public readonly ushort xdrStABNt; + /// 0x080, Allocation blocks used by embedded volume + public readonly ushort xdrNumABlks; + + // If not + /// 0x07C, Size in blocks of volume cache + public readonly ushort drVCSize; + /// 0x07E, Size in blocks of volume bitmap cache + public readonly ushort drVBMCSize; + /// 0x080, Size in blocks of volume common cache + public readonly ushort drCtlCSize; + + // End of variable variables :D + /// 0x082, Bytes in the extents B-Tree 3 HFS extents following, 32 bits each + public readonly uint drXTFlSize; + /// 0x092, Bytes in the catalog B-Tree 3 HFS extents following, 32 bits each + public readonly uint drCTFlSize; + } + } +} \ No newline at end of file diff --git a/DiscImageChef.Filesystems/DiscImageChef.Filesystems.csproj b/DiscImageChef.Filesystems/DiscImageChef.Filesystems.csproj index 26cebd894..e486e0b01 100644 --- a/DiscImageChef.Filesystems/DiscImageChef.Filesystems.csproj +++ b/DiscImageChef.Filesystems/DiscImageChef.Filesystems.csproj @@ -54,6 +54,10 @@ + + + + @@ -88,7 +92,6 @@ -