From 99c5e5d9d0de8ace642cb601441552c842367d8e Mon Sep 17 00:00:00 2001 From: Natalia Portillo Date: Wed, 26 Jul 2017 04:21:47 +0100 Subject: [PATCH] Added support for HAMMER filesystem. --- .../DiscImageChef.Filesystems.csproj | 1 + DiscImageChef.Filesystems/HAMMER.cs | 284 ++++++++++-------- DiscImageChef.Tests/Filesystems/HAMMER_MBR.cs | 12 +- README.md | 1 + 4 files changed, 162 insertions(+), 136 deletions(-) diff --git a/DiscImageChef.Filesystems/DiscImageChef.Filesystems.csproj b/DiscImageChef.Filesystems/DiscImageChef.Filesystems.csproj index ae8642c65..c9f4b556d 100644 --- a/DiscImageChef.Filesystems/DiscImageChef.Filesystems.csproj +++ b/DiscImageChef.Filesystems/DiscImageChef.Filesystems.csproj @@ -126,6 +126,7 @@ + diff --git a/DiscImageChef.Filesystems/HAMMER.cs b/DiscImageChef.Filesystems/HAMMER.cs index 41a34bd40..6daa71e8f 100644 --- a/DiscImageChef.Filesystems/HAMMER.cs +++ b/DiscImageChef.Filesystems/HAMMER.cs @@ -35,56 +35,51 @@ using System.Collections.Generic; using System.Runtime.InteropServices; using System.Text; using DiscImageChef.CommonTypes; +using hammer_tid_t = System.UInt64; +using hammer_off_t = System.UInt64; +using hammer_crc_t = System.UInt32; namespace DiscImageChef.Filesystems { - // Information from Practical Filesystem Design, ISBN 1-55860-497-9 - public class AtheOS : Filesystem + public class HAMMER : Filesystem { - // Little endian constants (that is, as read by .NET :p) - const uint AFS_MAGIC1 = 0x41465331; - const uint AFS_MAGIC2 = 0xDD121031; - const uint AFS_MAGIC3 = 0x15B6830E; - // Common constants - const uint AFS_SUPERBLOCK_SIZE = 1024; - const uint AFS_BOOTBLOCK_SIZE = AFS_SUPERBLOCK_SIZE; + const ulong HAMMER_FSBUF_VOLUME = 0xC8414D4DC5523031; + const ulong HAMMER_FSBUF_VOLUME_REV = 0x313052C54D4D41C8; + const uint HAMMER_VOLHDR_SIZE = 1928; + const int HAMMER_BIGBLOCK_SIZE = 8192 * 1024; - public AtheOS() + public HAMMER() { - Name = "AtheOS Filesystem"; - PluginUUID = new Guid("AAB2C4F1-DC07-49EE-A948-576CC51B58C5"); + Name = "HAMMER Filesystem"; + PluginUUID = new Guid("91A188BF-5FD7-4677-BBD3-F59EBA9C864D"); CurrentEncoding = Encoding.GetEncoding("iso-8859-15"); } - public AtheOS(ImagePlugins.ImagePlugin imagePlugin, Partition partition, Encoding encoding) + public HAMMER(ImagePlugins.ImagePlugin imagePlugin, Partition partition, Encoding encoding) { - Name = "AtheOS Filesystem"; - PluginUUID = new Guid("AAB2C4F1-DC07-49EE-A948-576CC51B58C5"); + Name = "HAMMER Filesystem"; + PluginUUID = new Guid("91A188BF-5FD7-4677-BBD3-F59EBA9C864D"); if(encoding == null) CurrentEncoding = Encoding.GetEncoding("iso-8859-15"); } public override bool Identify(ImagePlugins.ImagePlugin imagePlugin, Partition partition) { - ulong sector = AFS_BOOTBLOCK_SIZE / imagePlugin.GetSectorSize(); - uint offset = AFS_BOOTBLOCK_SIZE % imagePlugin.GetSectorSize(); - uint run = 1; + uint run = HAMMER_VOLHDR_SIZE / imagePlugin.GetSectorSize(); - if(imagePlugin.GetSectorSize() < AFS_SUPERBLOCK_SIZE) - run = AFS_SUPERBLOCK_SIZE / imagePlugin.GetSectorSize(); + if(HAMMER_VOLHDR_SIZE % imagePlugin.GetSectorSize() > 0) + run++; - if((sector + partition.Start) >= partition.End) + if((run + partition.Start) >= partition.End) return false; - uint magic; + ulong magic; - byte[] tmp = imagePlugin.ReadSectors(sector + partition.Start, run); - byte[] sb_sector = new byte[AFS_SUPERBLOCK_SIZE]; - Array.Copy(tmp, offset, sb_sector, 0, AFS_SUPERBLOCK_SIZE); + byte[] sb_sector = imagePlugin.ReadSectors(partition.Start, run); - magic = BitConverter.ToUInt32(sb_sector, 0x20); + magic = BitConverter.ToUInt64(sb_sector, 0); - return magic == AFS_MAGIC1; + return magic == HAMMER_FSBUF_VOLUME || magic == HAMMER_FSBUF_VOLUME_REV; } public override void GetInformation(ImagePlugins.ImagePlugin imagePlugin, Partition partition, out string information) @@ -93,127 +88,157 @@ namespace DiscImageChef.Filesystems StringBuilder sb = new StringBuilder(); - AtheosSuperBlock afs_sb = new AtheosSuperBlock(); + HammerSuperBlock hammer_sb = new HammerSuperBlock(); - ulong sector = AFS_BOOTBLOCK_SIZE / imagePlugin.GetSectorSize(); - uint offset = AFS_BOOTBLOCK_SIZE % imagePlugin.GetSectorSize(); - uint run = 1; + uint run = HAMMER_VOLHDR_SIZE / imagePlugin.GetSectorSize(); - if(imagePlugin.GetSectorSize() < AFS_SUPERBLOCK_SIZE) - run = AFS_SUPERBLOCK_SIZE / imagePlugin.GetSectorSize(); + if(HAMMER_VOLHDR_SIZE % imagePlugin.GetSectorSize() > 0) + run++; - byte[] tmp = imagePlugin.ReadSectors(sector + partition.Start, run); - byte[] sb_sector = new byte[AFS_SUPERBLOCK_SIZE]; - Array.Copy(tmp, offset, sb_sector, 0, AFS_SUPERBLOCK_SIZE); + ulong magic; - GCHandle handle = GCHandle.Alloc(sb_sector, GCHandleType.Pinned); - afs_sb = (AtheosSuperBlock)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(AtheosSuperBlock)); - handle.Free(); + byte[] sb_sector = imagePlugin.ReadSectors(partition.Start, run); - sb.AppendLine("Atheos filesystem"); + magic = BitConverter.ToUInt64(sb_sector, 0); - if(afs_sb.flags == 1) - sb.AppendLine("Filesystem is read-only"); + if(magic == HAMMER_FSBUF_VOLUME) + { + GCHandle handle = GCHandle.Alloc(sb_sector, GCHandleType.Pinned); + hammer_sb = (HammerSuperBlock)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(HammerSuperBlock)); + handle.Free(); + } + else + { + hammer_sb = BigEndianMarshal.ByteArrayToStructureBigEndian(sb_sector); + } - sb.AppendFormat("Volume name: {0}", StringHandlers.CToString(afs_sb.name, CurrentEncoding)).AppendLine(); - sb.AppendFormat("{0} bytes per block", afs_sb.block_size).AppendLine(); - sb.AppendFormat("{0} blocks in volume ({1} bytes)", afs_sb.num_blocks, afs_sb.num_blocks * afs_sb.block_size).AppendLine(); - sb.AppendFormat("{0} used blocks ({1} bytes)", afs_sb.used_blocks, afs_sb.used_blocks * afs_sb.block_size).AppendLine(); - sb.AppendFormat("{0} bytes per i-node", afs_sb.inode_size).AppendLine(); - sb.AppendFormat("{0} blocks per allocation group ({1} bytes)", afs_sb.blocks_per_ag, afs_sb.blocks_per_ag * afs_sb.block_size).AppendLine(); - sb.AppendFormat("{0} allocation groups in volume", afs_sb.num_ags).AppendLine(); - sb.AppendFormat("Journal resides in block {0} of allocation group {1} and runs for {2} blocks ({3} bytes)", afs_sb.log_blocks_start, - afs_sb.log_blocks_ag, afs_sb.log_blocks_len, afs_sb.log_blocks_len * afs_sb.block_size).AppendLine(); - sb.AppendFormat("Journal starts in byte {0} and has {1} bytes in {2} blocks", afs_sb.log_start, afs_sb.log_size, afs_sb.log_valid_blocks).AppendLine(); - sb.AppendFormat("Root folder's i-node resides in block {0} of allocation group {1} and runs for {2} blocks ({3} bytes)", afs_sb.root_dir_start, - afs_sb.root_dir_ag, afs_sb.root_dir_len, afs_sb.root_dir_len * afs_sb.block_size).AppendLine(); - sb.AppendFormat("Directory containing files scheduled for deletion's i-node resides in block {0} of allocation group {1} and runs for {2} blocks ({3} bytes)", afs_sb.deleted_start, - afs_sb.deleted_ag, afs_sb.deleted_len, afs_sb.deleted_len * afs_sb.block_size).AppendLine(); - sb.AppendFormat("Indices' i-node resides in block {0} of allocation group {1} and runs for {2} blocks ({3} bytes)", afs_sb.indices_start, - afs_sb.indices_ag, afs_sb.indices_len, afs_sb.indices_len * afs_sb.block_size).AppendLine(); - sb.AppendFormat("{0} blocks for bootloader ({1} bytes)", afs_sb.boot_size, afs_sb.boot_size * afs_sb.block_size).AppendLine(); + sb.AppendLine("HAMMER filesystem"); - information = sb.ToString(); + sb.AppendFormat("Volume version: {0}", hammer_sb.vol_version).AppendLine(); + sb.AppendFormat("Volume {0} of {1} on this filesystem", hammer_sb.vol_no + 1, hammer_sb.vol_count).AppendLine(); + sb.AppendFormat("Volume name: {0}", StringHandlers.CToString(hammer_sb.vol_label, CurrentEncoding)).AppendLine(); + sb.AppendFormat("Volume serial: {0}", hammer_sb.vol_fsid).AppendLine(); + sb.AppendFormat("Filesystem type: {0}", hammer_sb.vol_fstype).AppendLine(); + sb.AppendFormat("Boot area starts at {0}", hammer_sb.vol_bot_beg).AppendLine(); + sb.AppendFormat("Memory log starts at {0}", hammer_sb.vol_mem_beg).AppendLine(); + sb.AppendFormat("First volume buffer starts at {0}", hammer_sb.vol_buf_beg).AppendLine(); + sb.AppendFormat("Volume ends at {0}", hammer_sb.vol_buf_end).AppendLine(); xmlFSType = new Schemas.FileSystemType { - Clusters = afs_sb.num_blocks, - ClusterSize = (int)afs_sb.block_size, + Clusters = (long)(partition.Size / HAMMER_BIGBLOCK_SIZE), + ClusterSize = HAMMER_BIGBLOCK_SIZE, Dirty = false, - FreeClusters = afs_sb.num_blocks - afs_sb.used_blocks, - FreeClustersSpecified = true, - Type = "AtheOS filesystem", - VolumeName = StringHandlers.CToString(afs_sb.name, CurrentEncoding) + Type = "HAMMER", + VolumeName = StringHandlers.CToString(hammer_sb.vol_label, CurrentEncoding), + VolumeSerial = hammer_sb.vol_fsid.ToString(), }; + + if(hammer_sb.vol_no == hammer_sb.vol_rootvol) + { + sb.AppendFormat("Filesystem contains {0} \"big-blocks\" ({1} bytes)", hammer_sb.vol0_stat_bigblocks, hammer_sb.vol0_stat_bigblocks * HAMMER_BIGBLOCK_SIZE).AppendLine(); + sb.AppendFormat("Filesystem has {0} \"big-blocks\" free ({1} bytes)", hammer_sb.vol0_stat_freebigblocks, hammer_sb.vol0_stat_freebigblocks * HAMMER_BIGBLOCK_SIZE).AppendLine(); + sb.AppendFormat("Filesystem has {0} inode used", hammer_sb.vol0_stat_inodes).AppendLine(); + + xmlFSType.Clusters = hammer_sb.vol0_stat_bigblocks; + xmlFSType.FreeClusters = hammer_sb.vol0_stat_freebigblocks; + xmlFSType.FreeClustersSpecified = true; + xmlFSType.Files = hammer_sb.vol0_stat_inodes; + xmlFSType.FilesSpecified = true; + } + // 0 ? + //sb.AppendFormat("Volume header CRC: 0x{0:X8}", afs_sb.vol_crc).AppendLine(); + + information = sb.ToString(); } /// /// Be superblock /// [StructLayout(LayoutKind.Sequential, Pack = 1)] - struct AtheosSuperBlock + struct HammerSuperBlock { - /// 0x000, Volume name, 32 bytes - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] - public byte[] name; - /// 0x020, "AFS1", 0x41465331 - public uint magic1; - /// 0x024, "BIGE", 0x42494745 - public uint fs_byte_order; - /// 0x028, Bytes per block - public uint block_size; - /// 0x02C, 1 << block_shift == block_size - public uint block_shift; - /// 0x030, Blocks in volume - public long num_blocks; - /// 0x038, Used blocks in volume - public long used_blocks; - /// 0x040, Bytes per inode - public int inode_size; - /// 0x044, 0xDD121031 - public uint magic2; - /// 0x048, Blocks per allocation group - public int blocks_per_ag; - /// 0x04C, 1 << ag_shift == blocks_per_ag - public int ag_shift; - /// 0x050, Allocation groups in volume - public int num_ags; - /// 0x054, 0x434c454e if clean, 0x44495254 if dirty - public uint flags; - /// 0x058, Allocation group of journal - public int log_blocks_ag; - /// 0x05C, Start block of journal, inside ag - public ushort log_blocks_start; - /// 0x05E, Length in blocks of journal, inside ag - public ushort log_blocks_len; - /// 0x060, Start of journal - public long log_start; - /// 0x068, Valid block logs - public int log_valid_blocks; - /// 0x06C, Log size - public int log_size; - /// 0x070, 0x15B6830E - public uint magic3; - /// 0x074, Allocation group where root folder's i-node resides - public int root_dir_ag; - /// 0x078, Start in ag of root folder's i-node - public ushort root_dir_start; - /// 0x07A, As this is part of inode_addr, this is 1 - public ushort root_dir_len; - /// 0x07C, Allocation group where pending-delete-files' i-node resides - public int deleted_ag; - /// 0x080, Start in ag of pending-delete-files' i-node - public ushort deleted_start; - /// 0x082, As this is part of inode_addr, this is 1 - public ushort deleted_len; - /// 0x084, Allocation group where indices' i-node resides - public int indices_ag; - /// 0x088, Start in ag of indices' i-node - public ushort indices_start; - /// 0x08A, As this is part of inode_addr, this is 1 - public ushort indices_len; - /// 0x08C, Size of bootloader - public int boot_size; + /// for a valid header + public ulong vol_signature; + + /* These are relative to block device offset, not zone offsets. */ + /// offset of boot area + public long vol_bot_beg; + /// offset of memory log + public long vol_mem_beg; + /// offset of the first buffer in volume + public long vol_buf_beg; + /// offset of volume EOF (on buffer boundary) + public long vol_buf_end; + public long vol_reserved01; + + /// identify filesystem + public Guid vol_fsid; + /// identify filesystem type + public Guid vol_fstype; + /// filesystem label + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)] + public byte[] vol_label; + + /// volume number within filesystem + public int vol_no; + /// number of volumes making up filesystem + public int vol_count; + + /// version control information + public uint vol_version; + /// header crc + public hammer_crc_t vol_crc; + /// volume flags + public uint vol_flags; + /// the root volume number (must be 0) + public uint vol_rootvol; + + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] + public uint[] vol_reserved; + + /* + * These fields are initialized and space is reserved in every + * volume making up a HAMMER filesytem, but only the root volume + * contains valid data. Note that vol0_stat_bigblocks does not + * include big-blocks for freemap and undomap initially allocated + * by newfs_hammer(8). + */ + /// total big-blocks when fs is empty + public long vol0_stat_bigblocks; + /// number of free big-blocks + public long vol0_stat_freebigblocks; + public long vol0_reserved01; + /// for statfs only + public long vol0_stat_inodes; + public long vol0_reserved02; + /// B-Tree root offset in zone-8 + public hammer_off_t vol0_btree_root; + /// highest partially synchronized TID + public hammer_tid_t vol0_next_tid; + public hammer_off_t vol0_reserved03; + + /// Blockmaps for zones. Not all zones use a blockmap. Note that the entire root blockmap is cached in the hammer_mount structure. + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] + public HammerBlockMap[] vol0_blockmap; + + /// Array of zone-2 addresses for undo FIFO. + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)] + public hammer_off_t[] vol0_undo_array; + } + + struct HammerBlockMap + { + /// zone-2 offset only used by zone-4 + public hammer_off_t phys_offset; + /// zone-X offset only used by zone-3 + public hammer_off_t first_offset; + /// zone-X offset for allocation + public hammer_off_t next_offset; + /// zone-X offset only used by zone-3 + public hammer_off_t alloc_offset; + public uint reserved01; + public hammer_crc_t entry_crc; } public override Errno Mount() @@ -276,4 +301,5 @@ namespace DiscImageChef.Filesystems return Errno.NotImplemented; } } -} \ No newline at end of file +} + \ No newline at end of file diff --git a/DiscImageChef.Tests/Filesystems/HAMMER_MBR.cs b/DiscImageChef.Tests/Filesystems/HAMMER_MBR.cs index e9297641f..308388ef2 100644 --- a/DiscImageChef.Tests/Filesystems/HAMMER_MBR.cs +++ b/DiscImageChef.Tests/Filesystems/HAMMER_MBR.cs @@ -64,11 +64,11 @@ namespace DiscImageChef.Tests.Filesystems }; readonly long[] clusters = { - 104856192, 104856192, + 6310, 6310, }; readonly int[] clustersize = { - 512, 512, + 8388608, 8388608, }; readonly string[] volumename = { @@ -76,14 +76,12 @@ namespace DiscImageChef.Tests.Filesystems }; readonly string[] volumeserial = { - "UNKNOWN","UNKNOWN", + "f8e1a8bb-626d-11e7-94b5-0900274691e4","ff4dc664-6276-11e7-983f-090027c41b46", }; [Test] public void Test() { - throw new NotImplementedException("HAMMER filesystem is not yet implemented"); - /* for(int i = 0; i < testfiles.Length; i++) { string location = Path.Combine(Consts.TestFilesRoot, "filesystems", "hammer_mbr", testfiles[i]); @@ -98,7 +96,7 @@ namespace DiscImageChef.Tests.Filesystems int part = -1; for(int j = 0; j < partitions.Count; j++) { - if(partitions[j].PartitionType == "0xA5") + if(partitions[j].Type == "Hammer") { part = j; break; @@ -112,7 +110,7 @@ namespace DiscImageChef.Tests.Filesystems Assert.AreEqual("HAMMER", fs.XmlFSType.Type, testfiles[i]); Assert.AreEqual(volumename[i], fs.XmlFSType.VolumeName, testfiles[i]); Assert.AreEqual(volumeserial[i], fs.XmlFSType.VolumeSerial, testfiles[i]); - }*/ + } } } } diff --git a/README.md b/README.md index f11728d97..5f71e1e91 100644 --- a/README.md +++ b/README.md @@ -118,6 +118,7 @@ Supported file systems for identification and information only * DEC Files-11 (only checked with On Disk Structure 2, ODS-2) * ECMA-67: 130mm Flexible Disk Cartridge Labelling and File Structure for Information Interchange * Flash-Friendly File System (F2FS) +* HAMMER file system * IBM Journaling File System (JFS) * ISO9660 * Linux extended file system