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 = BigEndianMarshal.ByteArrayToStructureBigEndian <HfsMasterDirectoryBlock>(mdbSector); HfsBootBlock bb = BigEndianMarshal.ByteArrayToStructureBigEndian <HfsBootBlock>(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 = (int)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}"; } }
public Errno Mount(IMediaImage imagePlugin, Partition partition, Encoding encoding, Dictionary <string, string> options) { device = imagePlugin; partitionStart = partition.Start; Encoding = encoding ?? Encoding.GetEncoding("macintosh"); if (options == null) { options = GetDefaultOptions(); } if (options.TryGetValue("debug", out string debugString)) { bool.TryParse(debugString, out debug); } volMDB = new MFS_MasterDirectoryBlock(); mdbBlocks = device.ReadSector(2 + partitionStart); bootBlocks = device.ReadSector(0 + partitionStart); BigEndianBitConverter.IsLittleEndian = BitConverter.IsLittleEndian; volMDB.drSigWord = BigEndianBitConverter.ToUInt16(mdbBlocks, 0x000); if (volMDB.drSigWord != MFS_MAGIC) { return(Errno.InvalidArgument); } volMDB.drCrDate = BigEndianBitConverter.ToUInt32(mdbBlocks, 0x002); volMDB.drLsBkUp = BigEndianBitConverter.ToUInt32(mdbBlocks, 0x006); volMDB.drAtrb = BigEndianBitConverter.ToUInt16(mdbBlocks, 0x00A); volMDB.drNmFls = BigEndianBitConverter.ToUInt16(mdbBlocks, 0x00C); volMDB.drDirSt = BigEndianBitConverter.ToUInt16(mdbBlocks, 0x00E); volMDB.drBlLen = BigEndianBitConverter.ToUInt16(mdbBlocks, 0x010); volMDB.drNmAlBlks = BigEndianBitConverter.ToUInt16(mdbBlocks, 0x012); volMDB.drAlBlkSiz = BigEndianBitConverter.ToUInt32(mdbBlocks, 0x014); volMDB.drClpSiz = BigEndianBitConverter.ToUInt32(mdbBlocks, 0x018); volMDB.drAlBlSt = BigEndianBitConverter.ToUInt16(mdbBlocks, 0x01C); volMDB.drNxtFNum = BigEndianBitConverter.ToUInt32(mdbBlocks, 0x01E); volMDB.drFreeBks = BigEndianBitConverter.ToUInt16(mdbBlocks, 0x022); volMDB.drVNSiz = mdbBlocks[0x024]; byte[] variableSize = new byte[volMDB.drVNSiz + 1]; Array.Copy(mdbBlocks, 0x024, variableSize, 0, volMDB.drVNSiz + 1); volMDB.drVN = StringHandlers.PascalToString(variableSize, Encoding); directoryBlocks = device.ReadSectors(volMDB.drDirSt + partitionStart, volMDB.drBlLen); int bytesInBlockMap = volMDB.drNmAlBlks * 12 / 8 + volMDB.drNmAlBlks * 12 % 8; const int BYTES_BEFORE_BLOCK_MAP = 64; int bytesInWholeMdb = bytesInBlockMap + BYTES_BEFORE_BLOCK_MAP; int sectorsInWholeMdb = bytesInWholeMdb / (int)device.Info.SectorSize + bytesInWholeMdb % (int)device.Info.SectorSize; byte[] wholeMdb = device.ReadSectors(partitionStart + 2, (uint)sectorsInWholeMdb); blockMapBytes = new byte[bytesInBlockMap]; Array.Copy(wholeMdb, BYTES_BEFORE_BLOCK_MAP, blockMapBytes, 0, blockMapBytes.Length); int offset = 0; blockMap = new uint[volMDB.drNmAlBlks + 2 + 1]; for (int i = 2; i < volMDB.drNmAlBlks + 2; i += 8) { uint tmp1 = 0; uint tmp2 = 0; uint tmp3 = 0; if (offset + 4 <= blockMapBytes.Length) { tmp1 = BigEndianBitConverter.ToUInt32(blockMapBytes, offset); } if (offset + 4 + 4 <= blockMapBytes.Length) { tmp2 = BigEndianBitConverter.ToUInt32(blockMapBytes, offset + 4); } if (offset + 8 + 4 <= blockMapBytes.Length) { tmp3 = BigEndianBitConverter.ToUInt32(blockMapBytes, offset + 8); } if (i < blockMap.Length) { blockMap[i] = (tmp1 & 0xFFF00000) >> 20; } if (i + 2 < blockMap.Length) { blockMap[i + 1] = (tmp1 & 0xFFF00) >> 8; } if (i + 3 < blockMap.Length) { blockMap[i + 2] = ((tmp1 & 0xFF) << 4) + ((tmp2 & 0xF0000000) >> 28); } if (i + 4 < blockMap.Length) { blockMap[i + 3] = (tmp2 & 0xFFF0000) >> 16; } if (i + 5 < blockMap.Length) { blockMap[i + 4] = (tmp2 & 0xFFF0) >> 4; } if (i + 6 < blockMap.Length) { blockMap[i + 5] = ((tmp2 & 0xF) << 8) + ((tmp3 & 0xFF000000) >> 24); } if (i + 7 < blockMap.Length) { blockMap[i + 6] = (tmp3 & 0xFFF000) >> 12; } if (i + 8 < blockMap.Length) { blockMap[i + 7] = tmp3 & 0xFFF; } offset += 12; } if (device.Info.ReadableSectorTags.Contains(SectorTagType.AppleSectorTag)) { mdbTags = device.ReadSectorTag(2 + partitionStart, SectorTagType.AppleSectorTag); bootTags = device.ReadSectorTag(0 + partitionStart, SectorTagType.AppleSectorTag); directoryTags = device.ReadSectorsTag(volMDB.drDirSt + partitionStart, volMDB.drBlLen, SectorTagType.AppleSectorTag); bitmapTags = device.ReadSectorsTag(partitionStart + 2, (uint)sectorsInWholeMdb, SectorTagType.AppleSectorTag); } sectorsPerBlock = (int)(volMDB.drAlBlkSiz / device.Info.SectorSize); if (!FillDirectory()) { return(Errno.InvalidArgument); } mounted = true; ushort bbSig = BigEndianBitConverter.ToUInt16(bootBlocks, 0x000); if (bbSig != MFSBB_MAGIC) { bootBlocks = null; } XmlFsType = new FileSystemType(); if (volMDB.drLsBkUp > 0) { XmlFsType.BackupDate = DateHandlers.MacToDateTime(volMDB.drLsBkUp); XmlFsType.BackupDateSpecified = true; } XmlFsType.Bootable = bbSig == MFSBB_MAGIC; XmlFsType.Clusters = volMDB.drNmAlBlks; XmlFsType.ClusterSize = (int)volMDB.drAlBlkSiz; if (volMDB.drCrDate > 0) { XmlFsType.CreationDate = DateHandlers.MacToDateTime(volMDB.drCrDate); XmlFsType.CreationDateSpecified = true; } XmlFsType.Files = volMDB.drNmFls; XmlFsType.FilesSpecified = true; XmlFsType.FreeClusters = volMDB.drFreeBks; XmlFsType.FreeClustersSpecified = true; XmlFsType.Type = "MFS"; XmlFsType.VolumeName = volMDB.drVN; return(Errno.NoError); }
public bool GetInformation(IMediaImage imagePlugin, out List <Partition> partitions, ulong sectorOffset) { partitions = new List <Partition>(); if (sectorOffset + 2 >= imagePlugin.Info.Sectors) { return(false); } byte[] hdrBytes = imagePlugin.ReadSector(1 + sectorOffset); GptHeader hdr; ulong signature = BitConverter.ToUInt64(hdrBytes, 0); bool misaligned = false; DicConsole.DebugWriteLine("GPT Plugin", "hdr.signature = 0x{0:X16}", signature); if (signature != GPT_MAGIC) { if (imagePlugin.Info.XmlMediaType == XmlMediaType.OpticalDisc) { hdrBytes = imagePlugin.ReadSector(sectorOffset); signature = BitConverter.ToUInt64(hdrBytes, 512); DicConsole.DebugWriteLine("GPT Plugin", "hdr.signature @ 0x200 = 0x{0:X16}", signature); if (signature == GPT_MAGIC) { DicConsole.DebugWriteLine("GPT Plugin", "Found unaligned signature", signature); byte[] real = new byte[512]; Array.Copy(hdrBytes, 512, real, 0, 512); hdrBytes = real; misaligned = true; } else { return(false); } } else { return(false); } } try { GCHandle handle = GCHandle.Alloc(hdrBytes, GCHandleType.Pinned); hdr = (GptHeader)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(GptHeader)); handle.Free(); } catch { return(false); } DicConsole.DebugWriteLine("GPT Plugin", "hdr.revision = 0x{0:X8}", hdr.revision); DicConsole.DebugWriteLine("GPT Plugin", "hdr.headerSize = {0}", hdr.headerSize); DicConsole.DebugWriteLine("GPT Plugin", "hdr.headerCrc = 0x{0:X8}", hdr.headerCrc); DicConsole.DebugWriteLine("GPT Plugin", "hdr.reserved = 0x{0:X8}", hdr.reserved); DicConsole.DebugWriteLine("GPT Plugin", "hdr.myLBA = {0}", hdr.myLBA); DicConsole.DebugWriteLine("GPT Plugin", "hdr.alternateLBA = {0}", hdr.alternateLBA); DicConsole.DebugWriteLine("GPT Plugin", "hdr.firstUsableLBA = {0}", hdr.firstUsableLBA); DicConsole.DebugWriteLine("GPT Plugin", "hdr.lastUsableLBA = {0}", hdr.lastUsableLBA); DicConsole.DebugWriteLine("GPT Plugin", "hdr.diskGuid = {0}", hdr.diskGuid); DicConsole.DebugWriteLine("GPT Plugin", "hdr.entryLBA = {0}", hdr.entryLBA); DicConsole.DebugWriteLine("GPT Plugin", "hdr.entries = {0}", hdr.entries); DicConsole.DebugWriteLine("GPT Plugin", "hdr.entriesSize = {0}", hdr.entriesSize); DicConsole.DebugWriteLine("GPT Plugin", "hdr.entriesCrc = 0x{0:X8}", hdr.entriesCrc); if (hdr.signature != GPT_MAGIC) { return(false); } if (hdr.myLBA != 1 + sectorOffset) { return(false); } uint divisor, modulo, sectorSize; if (misaligned) { divisor = 4; modulo = (uint)(hdr.entryLBA % divisor); sectorSize = 512; } else { divisor = 1; modulo = 0; sectorSize = imagePlugin.Info.SectorSize; } uint totalEntriesSectors = hdr.entries * hdr.entriesSize / imagePlugin.Info.SectorSize; if (hdr.entries * hdr.entriesSize % imagePlugin.Info.SectorSize > 0) { totalEntriesSectors++; } byte[] temp = imagePlugin.ReadSectors(hdr.entryLBA / divisor, totalEntriesSectors + modulo); byte[] entriesBytes = new byte[temp.Length - modulo * 512]; Array.Copy(temp, modulo * 512, entriesBytes, 0, entriesBytes.Length); List <GptEntry> entries = new List <GptEntry>(); for (int i = 0; i < hdr.entries; i++) { try { byte[] entryBytes = new byte[hdr.entriesSize]; Array.Copy(entriesBytes, hdr.entriesSize * i, entryBytes, 0, hdr.entriesSize); GCHandle handle = GCHandle.Alloc(entryBytes, GCHandleType.Pinned); GptEntry entry = (GptEntry)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(GptEntry)); handle.Free(); entries.Add(entry); } #pragma warning disable RECS0022 // A catch clause that catches System.Exception and has an empty body catch { // ignored } #pragma warning restore RECS0022 // A catch clause that catches System.Exception and has an empty body } if (entries.Count == 0) { return(false); } ulong pseq = 0; foreach (GptEntry entry in entries.Where(entry => entry.partitionType != Guid.Empty && entry.partitionId != Guid.Empty)) { DicConsole.DebugWriteLine("GPT Plugin", "entry.partitionType = {0}", entry.partitionType); DicConsole.DebugWriteLine("GPT Plugin", "entry.partitionId = {0}", entry.partitionId); DicConsole.DebugWriteLine("GPT Plugin", "entry.startLBA = {0}", entry.startLBA); DicConsole.DebugWriteLine("GPT Plugin", "entry.endLBA = {0}", entry.endLBA); DicConsole.DebugWriteLine("GPT Plugin", "entry.attributes = 0x{0:X16}", entry.attributes); DicConsole.DebugWriteLine("GPT Plugin", "entry.name = {0}", entry.name); if (entry.startLBA / divisor > imagePlugin.Info.Sectors || entry.endLBA / divisor > imagePlugin.Info.Sectors) { return(false); } Partition part = new Partition { Description = $"ID: {entry.partitionId}", Size = (entry.endLBA - entry.startLBA + 1) * sectorSize, Name = entry.name, Length = (entry.endLBA - entry.startLBA + 1) / divisor, Sequence = pseq++, Offset = entry.startLBA * sectorSize, Start = entry.startLBA / divisor, Type = GetGuidTypeName(entry.partitionType), Scheme = Name }; DicConsole.DebugWriteLine("GPT Plugin", "part.PartitionType = {0}", part.Type); partitions.Add(part); } return(true); }
public bool Identify(IMediaImage imagePlugin, Partition partition) { if (imagePlugin.Info.SectorSize < 512) { return(false); } // Misaligned if (imagePlugin.Info.XmlMediaType == XmlMediaType.OpticalDisc) { uint sbSize = (uint)((Marshal.SizeOf <EFS_Superblock>() + 0x200) / imagePlugin.Info.SectorSize); if ((Marshal.SizeOf <EFS_Superblock>() + 0x200) % imagePlugin.Info.SectorSize != 0) { sbSize++; } byte[] sector = imagePlugin.ReadSectors(partition.Start, sbSize); if (sector.Length < Marshal.SizeOf <EFS_Superblock>()) { return(false); } byte[] sbpiece = new byte[Marshal.SizeOf <EFS_Superblock>()]; Array.Copy(sector, 0x200, sbpiece, 0, Marshal.SizeOf <EFS_Superblock>()); EFS_Superblock efsSb = Marshal.ByteArrayToStructureBigEndian <EFS_Superblock>(sbpiece); DicConsole.DebugWriteLine("EFS plugin", "magic at 0x{0:X3} = 0x{1:X8} (expected 0x{2:X8} or 0x{3:X8})", 0x200, efsSb.sb_magic, EFS_MAGIC, EFS_MAGIC_NEW); if (efsSb.sb_magic == EFS_MAGIC || efsSb.sb_magic == EFS_MAGIC_NEW) { return(true); } } else { uint sbSize = (uint)(Marshal.SizeOf <EFS_Superblock>() / imagePlugin.Info.SectorSize); if (Marshal.SizeOf <EFS_Superblock>() % imagePlugin.Info.SectorSize != 0) { sbSize++; } byte[] sector = imagePlugin.ReadSectors(partition.Start + 1, sbSize); if (sector.Length < Marshal.SizeOf <EFS_Superblock>()) { return(false); } EFS_Superblock efsSb = Marshal.ByteArrayToStructureBigEndian <EFS_Superblock>(sector); DicConsole.DebugWriteLine("EFS plugin", "magic at {0} = 0x{1:X8} (expected 0x{2:X8} or 0x{3:X8})", 1, efsSb.sb_magic, EFS_MAGIC, EFS_MAGIC_NEW); if (efsSb.sb_magic == EFS_MAGIC || efsSb.sb_magic == EFS_MAGIC_NEW) { return(true); } } return(false); }
public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, Encoding encoding) { Encoding = encoding ?? Encoding.UTF8; information = ""; if (imagePlugin.Info.SectorSize < 512) { return; } uint sbAddr = NILFS2_SUPER_OFFSET / imagePlugin.Info.SectorSize; if (sbAddr == 0) { sbAddr = 1; } uint sbSize = (uint)(Marshal.SizeOf <NILFS2_Superblock>() / imagePlugin.Info.SectorSize); if (Marshal.SizeOf <NILFS2_Superblock>() % imagePlugin.Info.SectorSize != 0) { sbSize++; } byte[] sector = imagePlugin.ReadSectors(partition.Start + sbAddr, sbSize); if (sector.Length < Marshal.SizeOf <NILFS2_Superblock>()) { return; } NILFS2_Superblock nilfsSb = Marshal.ByteArrayToStructureLittleEndian <NILFS2_Superblock>(sector); if (nilfsSb.magic != NILFS2_MAGIC) { return; } StringBuilder sb = new StringBuilder(); sb.AppendLine("NILFS2 filesystem"); sb.AppendFormat("Version {0}.{1}", nilfsSb.rev_level, nilfsSb.minor_rev_level).AppendLine(); sb.AppendFormat("{0} bytes per block", 1 << (int)(nilfsSb.log_block_size + 10)).AppendLine(); sb.AppendFormat("{0} bytes in volume", nilfsSb.dev_size).AppendLine(); sb.AppendFormat("{0} blocks per segment", nilfsSb.blocks_per_segment).AppendLine(); sb.AppendFormat("{0} segments", nilfsSb.nsegments).AppendLine(); if (nilfsSb.creator_os == 0) { sb.AppendLine("Filesystem created on Linux"); } else { sb.AppendFormat("Creator OS code: {0}", nilfsSb.creator_os).AppendLine(); } sb.AppendFormat("{0} bytes per inode", nilfsSb.inode_size).AppendLine(); sb.AppendFormat("Volume UUID: {0}", nilfsSb.uuid).AppendLine(); sb.AppendFormat("Volume name: {0}", StringHandlers.CToString(nilfsSb.volume_name, Encoding)).AppendLine(); sb.AppendFormat("Volume created on {0}", DateHandlers.UnixUnsignedToDateTime(nilfsSb.ctime)).AppendLine(); sb.AppendFormat("Volume last mounted on {0}", DateHandlers.UnixUnsignedToDateTime(nilfsSb.mtime)) .AppendLine(); sb.AppendFormat("Volume last written on {0}", DateHandlers.UnixUnsignedToDateTime(nilfsSb.wtime)) .AppendLine(); information = sb.ToString(); XmlFsType = new FileSystemType { Type = "NILFS2 filesystem", ClusterSize = (uint)(1 << (int)(nilfsSb.log_block_size + 10)), VolumeName = StringHandlers.CToString(nilfsSb.volume_name, Encoding), VolumeSerial = nilfsSb.uuid.ToString(), CreationDate = DateHandlers.UnixUnsignedToDateTime(nilfsSb.ctime), CreationDateSpecified = true, ModificationDate = DateHandlers.UnixUnsignedToDateTime(nilfsSb.wtime), ModificationDateSpecified = true }; if (nilfsSb.creator_os == 0) { XmlFsType.SystemIdentifier = "Linux"; } XmlFsType.Clusters = nilfsSb.dev_size / XmlFsType.ClusterSize; }
public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, Encoding encoding) { Encoding = encoding ?? Encoding.GetEncoding("iso-8859-15"); information = ""; var sb = new StringBuilder(); SuperBlock superBlock; uint run = HAMMER_VOLHDR_SIZE / imagePlugin.Info.SectorSize; if (HAMMER_VOLHDR_SIZE % imagePlugin.Info.SectorSize > 0) { run++; } byte[] sbSector = imagePlugin.ReadSectors(partition.Start, run); ulong magic = BitConverter.ToUInt64(sbSector, 0); superBlock = magic == HAMMER_FSBUF_VOLUME?Marshal.ByteArrayToStructureLittleEndian <SuperBlock>(sbSector) : Marshal.ByteArrayToStructureBigEndian <SuperBlock>(sbSector); sb.AppendLine("HAMMER filesystem"); sb.AppendFormat("Volume version: {0}", superBlock.vol_version).AppendLine(); sb.AppendFormat("Volume {0} of {1} on this filesystem", superBlock.vol_no + 1, superBlock.vol_count). AppendLine(); sb.AppendFormat("Volume name: {0}", StringHandlers.CToString(superBlock.vol_label, Encoding)).AppendLine(); sb.AppendFormat("Volume serial: {0}", superBlock.vol_fsid).AppendLine(); sb.AppendFormat("Filesystem type: {0}", superBlock.vol_fstype).AppendLine(); sb.AppendFormat("Boot area starts at {0}", superBlock.vol_bot_beg).AppendLine(); sb.AppendFormat("Memory log starts at {0}", superBlock.vol_mem_beg).AppendLine(); sb.AppendFormat("First volume buffer starts at {0}", superBlock.vol_buf_beg).AppendLine(); sb.AppendFormat("Volume ends at {0}", superBlock.vol_buf_end).AppendLine(); XmlFsType = new FileSystemType { Clusters = partition.Size / HAMMER_BIGBLOCK_SIZE, ClusterSize = HAMMER_BIGBLOCK_SIZE, Dirty = false, Type = "HAMMER", VolumeName = StringHandlers.CToString(superBlock.vol_label, Encoding), VolumeSerial = superBlock.vol_fsid.ToString() }; if (superBlock.vol_no == superBlock.vol_rootvol) { sb.AppendFormat("Filesystem contains {0} \"big-blocks\" ({1} bytes)", superBlock.vol0_stat_bigblocks, superBlock.vol0_stat_bigblocks * HAMMER_BIGBLOCK_SIZE).AppendLine(); sb.AppendFormat("Filesystem has {0} \"big-blocks\" free ({1} bytes)", superBlock.vol0_stat_freebigblocks, superBlock.vol0_stat_freebigblocks * HAMMER_BIGBLOCK_SIZE).AppendLine(); sb.AppendFormat("Filesystem has {0} inode used", superBlock.vol0_stat_inodes).AppendLine(); XmlFsType.Clusters = (ulong)superBlock.vol0_stat_bigblocks; XmlFsType.FreeClusters = (ulong)superBlock.vol0_stat_freebigblocks; XmlFsType.FreeClustersSpecified = true; XmlFsType.Files = (ulong)superBlock.vol0_stat_inodes; XmlFsType.FilesSpecified = true; } // 0 ? //sb.AppendFormat("Volume header CRC: 0x{0:X8}", afs_sb.vol_crc).AppendLine(); information = sb.ToString(); }
public bool Identify(IMediaImage imagePlugin, Partition partition) { if (partition.Start >= partition.End) { return(false); } // Boot block is unless defined otherwise, 2 blocks // Funny, you may need boot block to find root block if it's not in standard place just to know size of // block size and then read the whole boot block. // However while you can set a block size different from the sector size on formatting, the bootblock block // size for floppies is the sector size, and for RDB is usually is the hard disk sector size, // so this is not entirely wrong... byte[] sector = imagePlugin.ReadSectors(0 + partition.Start, 2); BootBlock bblk = Marshal.ByteArrayToStructureBigEndian <BootBlock>(sector); // AROS boot floppies... if (sector.Length >= 512 && sector[510] == 0x55 && sector[511] == 0xAA && (bblk.diskType & FFS_MASK) != FFS_MASK && (bblk.diskType & MUFS_MASK) != MUFS_MASK) { sector = imagePlugin.ReadSectors(1 + partition.Start, 2); bblk = Marshal.ByteArrayToStructureBigEndian <BootBlock>(sector); } // Not FFS or MuFS? if ((bblk.diskType & FFS_MASK) != FFS_MASK && (bblk.diskType & MUFS_MASK) != MUFS_MASK) { return(false); } // Clear checksum on sector sector[4] = sector[5] = sector[6] = sector[7] = 0; uint bsum = AmigaBootChecksum(sector); AaruConsole.DebugWriteLine("AmigaDOS plugin", "bblk.checksum = 0x{0:X8}", bblk.checksum); AaruConsole.DebugWriteLine("AmigaDOS plugin", "bsum = 0x{0:X8}", bsum); ulong bRootPtr = 0; // If bootblock is correct, let's take its rootblock pointer if (bsum == bblk.checksum) { bRootPtr = bblk.root_ptr + partition.Start; AaruConsole.DebugWriteLine("AmigaDOS plugin", "Bootblock points to {0} as Rootblock", bRootPtr); } ulong[] rootPtrs = { bRootPtr + partition.Start, ((((partition.End - partition.Start) + 1) / 2) + partition.Start) - 2, ((((partition.End - partition.Start) + 1) / 2) + partition.Start) - 1, (((partition.End - partition.Start) + 1) / 2) + partition.Start, (((partition.End - partition.Start) + 1) / 2) + partition.Start + 4 }; var rblk = new RootBlock(); // So to handle even number of sectors foreach (ulong rootPtr in rootPtrs.Where(rootPtr => rootPtr < partition.End && rootPtr >= partition.Start)) { AaruConsole.DebugWriteLine("AmigaDOS plugin", "Searching for Rootblock in sector {0}", rootPtr); sector = imagePlugin.ReadSector(rootPtr); rblk.type = BigEndianBitConverter.ToUInt32(sector, 0x00); AaruConsole.DebugWriteLine("AmigaDOS plugin", "rblk.type = {0}", rblk.type); if (rblk.type != TYPE_HEADER) { continue; } rblk.hashTableSize = BigEndianBitConverter.ToUInt32(sector, 0x0C); AaruConsole.DebugWriteLine("AmigaDOS plugin", "rblk.hashTableSize = {0}", rblk.hashTableSize); uint blockSize = (rblk.hashTableSize + 56) * 4; uint sectorsPerBlock = (uint)(blockSize / sector.Length); AaruConsole.DebugWriteLine("AmigaDOS plugin", "blockSize = {0}", blockSize); AaruConsole.DebugWriteLine("AmigaDOS plugin", "sectorsPerBlock = {0}", sectorsPerBlock); if (blockSize % sector.Length > 0) { sectorsPerBlock++; } if (rootPtr + sectorsPerBlock >= partition.End) { continue; } sector = imagePlugin.ReadSectors(rootPtr, sectorsPerBlock); // Clear checksum on sector rblk.checksum = BigEndianBitConverter.ToUInt32(sector, 20); sector[20] = sector[21] = sector[22] = sector[23] = 0; uint rsum = AmigaChecksum(sector); AaruConsole.DebugWriteLine("AmigaDOS plugin", "rblk.checksum = 0x{0:X8}", rblk.checksum); AaruConsole.DebugWriteLine("AmigaDOS plugin", "rsum = 0x{0:X8}", rsum); rblk.sec_type = BigEndianBitConverter.ToUInt32(sector, sector.Length - 4); AaruConsole.DebugWriteLine("AmigaDOS plugin", "rblk.sec_type = {0}", rblk.sec_type); if (rblk.sec_type == SUBTYPE_ROOT && rblk.checksum == rsum) { return(true); } } return(false); }
public bool Identify(IMediaImage imagePlugin, Partition partition) { if (2 + partition.Start >= partition.End) { return(false); } byte sb_size_in_sectors; if (imagePlugin.Info.SectorSize <= 0x400 ) // Check if underlying device sector size is smaller than SuperBlock size { sb_size_in_sectors = (byte)(0x400 / imagePlugin.Info.SectorSize); } else { sb_size_in_sectors = 1; // If not a single sector can store it } if (partition.End <= partition.Start + 4 * (ulong)sb_size_in_sectors + sb_size_in_sectors ) // Device must be bigger than SB location + SB size + offset { return(false); } // Sectors in a cylinder int spc = (int)(imagePlugin.Info.Heads * imagePlugin.Info.SectorsPerTrack); // Superblock can start on 0x000, 0x200, 0x600 and 0x800, not aligned, so we assume 16 (128 bytes/sector) sectors as a safe value int[] locations = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, // Superblock can also skip one cylinder (for boot) spc }; foreach (byte[] sb_sector in locations.TakeWhile(i => (ulong)i + partition.Start + sb_size_in_sectors < imagePlugin.Info.Sectors) .Select(i => imagePlugin.ReadSectors((ulong)i + partition.Start, sb_size_in_sectors))) { uint magic = BitConverter.ToUInt32(sb_sector, 0x3F8); if (magic == XENIX_MAGIC || magic == XENIX_CIGAM || magic == SYSV_MAGIC || magic == SYSV_CIGAM) { return(true); } magic = BitConverter.ToUInt32(sb_sector, 0x1F8); // System V magic location if (magic == SYSV_MAGIC || magic == SYSV_CIGAM) { return(true); } magic = BitConverter.ToUInt32(sb_sector, 0x1F0); // XENIX 3 magic location if (magic == XENIX_MAGIC || magic == XENIX_CIGAM) { return(true); } byte[] coherent_string = new byte[6]; Array.Copy(sb_sector, 0x1E4, coherent_string, 0, 6); // Coherent UNIX s_fname location string s_fname = StringHandlers.CToString(coherent_string); Array.Copy(sb_sector, 0x1EA, coherent_string, 0, 6); // Coherent UNIX s_fpack location string s_fpack = StringHandlers.CToString(coherent_string); if (s_fname == COH_FNAME && s_fpack == COH_FPACK || s_fname == COH_XXXXX && s_fpack == COH_XXXXX || s_fname == COH_XXXXS && s_fpack == COH_XXXXN) { return(true); } // Now try to identify 7th edition uint s_fsize = BitConverter.ToUInt32(sb_sector, 0x002); ushort s_nfree = BitConverter.ToUInt16(sb_sector, 0x006); ushort s_ninode = BitConverter.ToUInt16(sb_sector, 0x0D0); if (s_fsize <= 0 || s_fsize >= 0xFFFFFFFF || s_nfree <= 0 || s_nfree >= 0xFFFF || s_ninode <= 0 || s_ninode >= 0xFFFF) { continue; } if ((s_fsize & 0xFF) == 0x00 && (s_nfree & 0xFF) == 0x00 && (s_ninode & 0xFF) == 0x00) { // Byteswap s_fsize = ((s_fsize & 0xFF) << 24) + ((s_fsize & 0xFF00) << 8) + ((s_fsize & 0xFF0000) >> 8) + ((s_fsize & 0xFF000000) >> 24); s_nfree = (ushort)(s_nfree >> 8); s_ninode = (ushort)(s_ninode >> 8); } if ((s_fsize & 0xFF000000) != 0x00 || (s_nfree & 0xFF00) != 0x00 || (s_ninode & 0xFF00) != 0x00) { continue; } if (s_fsize >= V7_MAXSIZE || s_nfree >= V7_NICFREE || s_ninode >= V7_NICINOD) { continue; } if (s_fsize * 1024 == (partition.End - partition.Start) * imagePlugin.Info.SectorSize || s_fsize * 512 == (partition.End - partition.Start) * imagePlugin.Info.SectorSize) { return(true); } } return(false); }
public bool GetInformation(IMediaImage imagePlugin, out List <Partition> partitions, ulong sectorOffset) { uint sectorSize; if (imagePlugin.Info.SectorSize == 2352 || imagePlugin.Info.SectorSize == 2448) { sectorSize = 2048; } else { sectorSize = imagePlugin.Info.SectorSize; } partitions = new List <Partition>(); if (sectorOffset + 2 >= imagePlugin.Info.Sectors) { return(false); } byte[] ddmSector = imagePlugin.ReadSector(sectorOffset); ushort maxDrivers = 61; if (sectorSize == 256) { byte[] tmp = new byte[512]; Array.Copy(ddmSector, 0, tmp, 0, 256); ddmSector = tmp; maxDrivers = 29; } else if (sectorSize < 256) { return(false); } AppleDriverDescriptorMap ddm = Marshal.ByteArrayToStructureBigEndian <AppleDriverDescriptorMap>(ddmSector); DicConsole.DebugWriteLine("AppleMap Plugin", "ddm.sbSig = 0x{0:X4}", ddm.sbSig); DicConsole.DebugWriteLine("AppleMap Plugin", "ddm.sbBlockSize = {0}", ddm.sbBlockSize); DicConsole.DebugWriteLine("AppleMap Plugin", "ddm.sbBlocks = {0}", ddm.sbBlocks); DicConsole.DebugWriteLine("AppleMap Plugin", "ddm.sbDevType = {0}", ddm.sbDevType); DicConsole.DebugWriteLine("AppleMap Plugin", "ddm.sbDevId = {0}", ddm.sbDevId); DicConsole.DebugWriteLine("AppleMap Plugin", "ddm.sbData = 0x{0:X8}", ddm.sbData); DicConsole.DebugWriteLine("AppleMap Plugin", "ddm.sbDrvrCount = {0}", ddm.sbDrvrCount); uint sequence = 0; if (ddm.sbSig == DDM_MAGIC) { if (ddm.sbDrvrCount < maxDrivers) { ddm.sbMap = new AppleDriverEntry[ddm.sbDrvrCount]; for (int i = 0; i < ddm.sbDrvrCount; i++) { byte[] tmp = new byte[8]; Array.Copy(ddmSector, 18 + i * 8, tmp, 0, 8); ddm.sbMap[i] = Marshal.ByteArrayToStructureBigEndian <AppleDriverEntry>(tmp); DicConsole.DebugWriteLine("AppleMap Plugin", "ddm.sbMap[{1}].ddBlock = {0}", ddm.sbMap[i].ddBlock, i); DicConsole.DebugWriteLine("AppleMap Plugin", "ddm.sbMap[{1}].ddSize = {0}", ddm.sbMap[i].ddSize, i); DicConsole.DebugWriteLine("AppleMap Plugin", "ddm.sbMap[{1}].ddType = {0}", ddm.sbMap[i].ddType, i); if (ddm.sbMap[i].ddSize == 0) { continue; } Partition part = new Partition { Size = (ulong)(ddm.sbMap[i].ddSize * 512), Length = (ulong)(ddm.sbMap[i].ddSize * 512 / sectorSize), Sequence = sequence, Offset = ddm.sbMap[i].ddBlock * sectorSize, Start = ddm.sbMap[i].ddBlock + sectorOffset, Type = "Apple_Driver" }; if (ddm.sbMap[i].ddSize * 512 % sectorSize > 0) { part.Length++; } partitions.Add(part); sequence++; } } } byte[] partSector = imagePlugin.ReadSector(1 + sectorOffset); AppleOldDevicePartitionMap oldMap = Marshal.ByteArrayToStructureBigEndian <AppleOldDevicePartitionMap>(partSector); // This is the easy one, no sector size mixing if (oldMap.pdSig == APM_MAGIC_OLD) { for (int i = 2; i < partSector.Length; i += 12) { byte[] tmp = new byte[12]; Array.Copy(partSector, i, tmp, 0, 12); AppleMapOldPartitionEntry oldEntry = Marshal.ByteArrayToStructureBigEndian <AppleMapOldPartitionEntry>(tmp); DicConsole.DebugWriteLine("AppleMap Plugin", "old_map.sbMap[{1}].pdStart = {0}", oldEntry.pdStart, (i - 2) / 12); DicConsole.DebugWriteLine("AppleMap Plugin", "old_map.sbMap[{1}].pdSize = {0}", oldEntry.pdSize, (i - 2) / 12); DicConsole.DebugWriteLine("AppleMap Plugin", "old_map.sbMap[{1}].pdFSID = 0x{0:X8}", oldEntry.pdFSID, (i - 2) / 12); if (oldEntry.pdSize == 0 && oldEntry.pdFSID == 0) { if (oldEntry.pdStart == 0) { break; } continue; } Partition part = new Partition { Size = oldEntry.pdStart * ddm.sbBlockSize, Length = oldEntry.pdStart * ddm.sbBlockSize / sectorSize, Sequence = sequence, Offset = oldEntry.pdSize * ddm.sbBlockSize, Start = oldEntry.pdSize * ddm.sbBlockSize / sectorSize, Scheme = Name, Type = oldEntry.pdFSID == HFS_MAGIC_OLD ? "Apple_HFS" : $"0x{oldEntry.pdFSID:X8}" }; partitions.Add(part); sequence++; } return(partitions.Count > 0); } AppleMapPartitionEntry entry; uint entrySize; uint entryCount; uint sectorsToRead; uint skipDdm; // If sector is bigger than 512 if (ddmSector.Length > 512) { byte[] tmp = new byte[512]; Array.Copy(ddmSector, 512, tmp, 0, 512); entry = Marshal.ByteArrayToStructureBigEndian <AppleMapPartitionEntry>(tmp); // Check for a partition entry that's 512-byte aligned if (entry.signature == APM_MAGIC) { DicConsole.DebugWriteLine("AppleMap Plugin", "Found misaligned entry."); entrySize = 512; entryCount = entry.entries; skipDdm = 512; sectorsToRead = (entryCount + 1) * 512 / sectorSize + 1; } else { entry = Marshal.ByteArrayToStructureBigEndian <AppleMapPartitionEntry>(partSector); if (entry.signature == APM_MAGIC) { DicConsole.DebugWriteLine("AppleMap Plugin", "Found aligned entry."); entrySize = sectorSize; entryCount = entry.entries; skipDdm = sectorSize; sectorsToRead = entryCount + 2; } else { return(partitions.Count > 0); } } } else { entry = Marshal.ByteArrayToStructureBigEndian <AppleMapPartitionEntry>(partSector); if (entry.signature == APM_MAGIC) { DicConsole.DebugWriteLine("AppleMap Plugin", "Found aligned entry."); entrySize = sectorSize; entryCount = entry.entries; skipDdm = sectorSize; sectorsToRead = entryCount + 2; } else { return(partitions.Count > 0); } } byte[] entries = imagePlugin.ReadSectors(sectorOffset, sectorsToRead); DicConsole.DebugWriteLine("AppleMap Plugin", "entry_size = {0}", entrySize); DicConsole.DebugWriteLine("AppleMap Plugin", "entry_count = {0}", entryCount); DicConsole.DebugWriteLine("AppleMap Plugin", "skip_ddm = {0}", skipDdm); DicConsole.DebugWriteLine("AppleMap Plugin", "sectors_to_read = {0}", sectorsToRead); byte[] copy = new byte[entries.Length - skipDdm]; Array.Copy(entries, skipDdm, copy, 0, copy.Length); entries = copy; for (int i = 0; i < entryCount; i++) { byte[] tmp = new byte[entrySize]; Array.Copy(entries, i * entrySize, tmp, 0, entrySize); entry = Marshal.ByteArrayToStructureBigEndian <AppleMapPartitionEntry>(tmp); if (entry.signature != APM_MAGIC) { continue; } DicConsole.DebugWriteLine("AppleMap Plugin", "dpme[{0}].signature = 0x{1:X4}", i, entry.signature); DicConsole.DebugWriteLine("AppleMap Plugin", "dpme[{0}].reserved1 = 0x{1:X4}", i, entry.reserved1); DicConsole.DebugWriteLine("AppleMap Plugin", "dpme[{0}].entries = {1}", i, entry.entries); DicConsole.DebugWriteLine("AppleMap Plugin", "dpme[{0}].start = {1}", i, entry.start); DicConsole.DebugWriteLine("AppleMap Plugin", "dpme[{0}].sectors = {1}", i, entry.sectors); DicConsole.DebugWriteLine("AppleMap Plugin", "dpme[{0}].name = \"{1}\"", i, StringHandlers.CToString(entry.name)); DicConsole.DebugWriteLine("AppleMap Plugin", "dpme[{0}].type = \"{1}\"", i, StringHandlers.CToString(entry.type)); DicConsole.DebugWriteLine("AppleMap Plugin", "dpme[{0}].first_data_block = {1}", i, entry.first_data_block); DicConsole.DebugWriteLine("AppleMap Plugin", "dpme[{0}].data_sectors = {1}", i, entry.data_sectors); DicConsole.DebugWriteLine("AppleMap Plugin", "dpme[{0}].flags = {1}", i, (AppleMapFlags)entry.flags); DicConsole.DebugWriteLine("AppleMap Plugin", "dpme[{0}].first_boot_block = {1}", i, entry.first_boot_block); DicConsole.DebugWriteLine("AppleMap Plugin", "dpme[{0}].boot_size = {1}", i, entry.boot_size); DicConsole.DebugWriteLine("AppleMap Plugin", "dpme[{0}].load_address = 0x{1:X8}", i, entry.load_address); DicConsole.DebugWriteLine("AppleMap Plugin", "dpme[{0}].load_address2 = 0x{1:X8}", i, entry.load_address2); DicConsole.DebugWriteLine("AppleMap Plugin", "dpme[{0}].entry_point = 0x{1:X8}", i, entry.entry_point); DicConsole.DebugWriteLine("AppleMap Plugin", "dpme[{0}].entry_point2 = 0x{1:X8}", i, entry.entry_point2); DicConsole.DebugWriteLine("AppleMap Plugin", "dpme[{0}].checksum = 0x{1:X8}", i, entry.checksum); DicConsole.DebugWriteLine("AppleMap Plugin", "dpme[{0}].processor = \"{1}\"", i, StringHandlers.CToString(entry.processor)); AppleMapFlags flags = (AppleMapFlags)entry.flags; // BeOS doesn't mark its partitions as valid //if(flags.HasFlag(AppleMapFlags.Valid) && if (StringHandlers.CToString(entry.type) == "Apple_partition_map" || entry.sectors <= 0) { continue; } StringBuilder sb = new StringBuilder(); Partition partition = new Partition { Sequence = sequence, Type = StringHandlers.CToString(entry.type), Name = StringHandlers.CToString(entry.name), Offset = entry.start * entrySize, Size = entry.sectors * entrySize, Start = entry.start * entrySize / sectorSize + sectorOffset, Length = entry.sectors * entrySize / sectorSize, Scheme = Name }; sb.AppendLine("Partition flags:"); if (flags.HasFlag(AppleMapFlags.Valid)) { sb.AppendLine("Partition is valid."); } if (flags.HasFlag(AppleMapFlags.Allocated)) { sb.AppendLine("Partition entry is allocated."); } if (flags.HasFlag(AppleMapFlags.InUse)) { sb.AppendLine("Partition is in use."); } if (flags.HasFlag(AppleMapFlags.Bootable)) { sb.AppendLine("Partition is bootable."); } if (flags.HasFlag(AppleMapFlags.Readable)) { sb.AppendLine("Partition is readable."); } if (flags.HasFlag(AppleMapFlags.Writable)) { sb.AppendLine("Partition is writable."); } if (flags.HasFlag(AppleMapFlags.Bootable)) { sb.AppendFormat("First boot sector: {0}", entry.first_boot_block * entrySize / sectorSize) .AppendLine(); sb.AppendFormat("Boot is {0} bytes.", entry.boot_size).AppendLine(); sb.AppendFormat("Boot load address: 0x{0:X8}", entry.load_address).AppendLine(); sb.AppendFormat("Boot entry point: 0x{0:X8}", entry.entry_point).AppendLine(); sb.AppendFormat("Boot code checksum: 0x{0:X8}", entry.checksum).AppendLine(); sb.AppendFormat("Processor: {0}", StringHandlers.CToString(entry.processor)).AppendLine(); if (flags.HasFlag(AppleMapFlags.PicCode)) { sb.AppendLine("Partition's boot code is position independent."); } } partition.Description = sb.ToString(); if (partition.Start < imagePlugin.Info.Sectors && partition.End < imagePlugin.Info.Sectors) { partitions.Add(partition); sequence++; } // Some CD and DVDs end with an Apple_Free that expands beyond the disc size... else if (partition.Start < imagePlugin.Info.Sectors) { DicConsole.DebugWriteLine("AppleMap Plugin", "Cutting last partition end ({0}) to media size ({1})", partition.End, imagePlugin.Info.Sectors - 1); partition.Length = imagePlugin.Info.Sectors - partition.Start; partitions.Add(partition); sequence++; } else { DicConsole.DebugWriteLine("AppleMap Plugin", "Not adding partition becaus start ({0}) is outside media size ({1})", partition.Start, imagePlugin.Info.Sectors - 1); } } return(partitions.Count > 0); }
public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, Encoding encoding) { Encoding = encoding ?? Encoding.ASCII; information = ""; var isoMetadata = new StringBuilder(); byte[] vdMagic = new byte[5]; // Volume Descriptor magic "CD001" byte[] hsMagic = new byte[5]; // Volume Descriptor magic "CDROM" string bootSpec = ""; PrimaryVolumeDescriptor?pvd = null; PrimaryVolumeDescriptor?jolietvd = null; BootRecord?bvd = null; HighSierraPrimaryVolumeDescriptor?hsvd = null; FileStructureVolumeDescriptor? fsvd = null; ElToritoBootRecord?torito = null; // ISO9660 is designed for 2048 bytes/sector devices if (imagePlugin.Info.SectorSize < 2048) { return; } // ISO9660 Primary Volume Descriptor starts at sector 16, so that's minimal size. if (partition.End < 16) { return; } ulong counter = 0; byte[] vdSector = imagePlugin.ReadSector(16 + counter + partition.Start); int xaOff = vdSector.Length == 2336 ? 8 : 0; Array.Copy(vdSector, 0x009 + xaOff, hsMagic, 0, 5); bool highSierraInfo = Encoding.GetString(hsMagic) == HIGH_SIERRA_MAGIC; int hsOff = 0; if (highSierraInfo) { hsOff = 8; } bool cdiInfo = false; bool evd = false; bool vpd = false; while (true) { AaruConsole.DebugWriteLine("ISO9660 plugin", "Processing VD loop no. {0}", counter); // Seek to Volume Descriptor AaruConsole.DebugWriteLine("ISO9660 plugin", "Reading sector {0}", 16 + counter + partition.Start); byte[] vdSectorTmp = imagePlugin.ReadSector(16 + counter + partition.Start); vdSector = new byte[vdSectorTmp.Length - xaOff]; Array.Copy(vdSectorTmp, xaOff, vdSector, 0, vdSector.Length); byte vdType = vdSector[0 + hsOff]; // Volume Descriptor Type, should be 1 or 2. AaruConsole.DebugWriteLine("ISO9660 plugin", "VDType = {0}", vdType); if (vdType == 255) // Supposedly we are in the PVD. { if (counter == 0) { return; } break; } Array.Copy(vdSector, 0x001, vdMagic, 0, 5); Array.Copy(vdSector, 0x009, hsMagic, 0, 5); if (Encoding.GetString(vdMagic) != ISO_MAGIC && Encoding.GetString(hsMagic) != HIGH_SIERRA_MAGIC && Encoding.GetString(vdMagic) != CDI_MAGIC ) // Recognized, it is an ISO9660, now check for rest of data. { if (counter == 0) { return; } break; } cdiInfo |= Encoding.GetString(vdMagic) == CDI_MAGIC; switch (vdType) { case 0: { bvd = Marshal.ByteArrayToStructureLittleEndian <BootRecord>(vdSector, hsOff, 2048 - hsOff); bootSpec = "Unknown"; if (Encoding.GetString(bvd.Value.system_id).Substring(0, 23) == "EL TORITO SPECIFICATION") { bootSpec = "El Torito"; torito = Marshal.ByteArrayToStructureLittleEndian <ElToritoBootRecord>(vdSector, hsOff, 2048 - hsOff); } break; } case 1: { if (highSierraInfo) { hsvd = Marshal. ByteArrayToStructureLittleEndian <HighSierraPrimaryVolumeDescriptor>(vdSector); } else if (cdiInfo) { fsvd = Marshal.ByteArrayToStructureBigEndian <FileStructureVolumeDescriptor>(vdSector); } else { pvd = Marshal.ByteArrayToStructureLittleEndian <PrimaryVolumeDescriptor>(vdSector); } break; } case 2: { PrimaryVolumeDescriptor svd = Marshal.ByteArrayToStructureLittleEndian <PrimaryVolumeDescriptor>(vdSector); // Check if this is Joliet if (svd.version == 1) { if (svd.escape_sequences[0] == '%' && svd.escape_sequences[1] == '/') { if (svd.escape_sequences[2] == '@' || svd.escape_sequences[2] == 'C' || svd.escape_sequences[2] == 'E') { jolietvd = svd; } else { AaruConsole.WriteLine("ISO9660 plugin", "Found unknown supplementary volume descriptor"); } } } else { evd = true; } break; } case 3: { vpd = true; break; } } counter++; } DecodedVolumeDescriptor decodedVd; var decodedJolietVd = new DecodedVolumeDescriptor(); XmlFsType = new FileSystemType(); if (pvd == null && hsvd == null && fsvd == null) { information = "ERROR: Could not find primary volume descriptor"; return; } if (highSierraInfo) { decodedVd = DecodeVolumeDescriptor(hsvd.Value); } else if (cdiInfo) { decodedVd = DecodeVolumeDescriptor(fsvd.Value); } else { decodedVd = DecodeVolumeDescriptor(pvd.Value); } if (jolietvd != null) { decodedJolietVd = DecodeJolietDescriptor(jolietvd.Value); } uint rootLocation = 0; uint rootSize = 0; // No need to read root on CD-i, as extensions are not supported... if (!cdiInfo) { rootLocation = highSierraInfo ? hsvd.Value.root_directory_record.extent : pvd.Value.root_directory_record.extent; if (highSierraInfo) { rootSize = hsvd.Value.root_directory_record.size / hsvd.Value.logical_block_size; if (hsvd.Value.root_directory_record.size % hsvd.Value.logical_block_size > 0) { rootSize++; } } else { rootSize = pvd.Value.root_directory_record.size / pvd.Value.logical_block_size; if (pvd.Value.root_directory_record.size % pvd.Value.logical_block_size > 0) { rootSize++; } } } byte[] rootDir = new byte[0]; int rootOff = 0; bool xaExtensions = false; bool apple = false; bool susp = false; bool rrip = false; bool ziso = false; bool amiga = false; bool aaip = false; List <ContinuationArea> contareas = new List <ContinuationArea>(); List <byte[]> refareas = new List <byte[]>(); var suspInformation = new StringBuilder(); if (rootLocation + rootSize < imagePlugin.Info.Sectors) { rootDir = imagePlugin.ReadSectors(rootLocation, rootSize); } // Walk thru root directory to see system area extensions in use while (rootOff + Marshal.SizeOf <DirectoryRecord>() < rootDir.Length && !cdiInfo) { DirectoryRecord record = Marshal.ByteArrayToStructureLittleEndian <DirectoryRecord>(rootDir, rootOff, Marshal.SizeOf <DirectoryRecord>()); int saOff = Marshal.SizeOf <DirectoryRecord>() + record.name_len; saOff += saOff % 2; int saLen = record.length - saOff; if (saLen > 0 && rootOff + saOff + saLen <= rootDir.Length) { byte[] sa = new byte[saLen]; Array.Copy(rootDir, rootOff + saOff, sa, 0, saLen); saOff = 0; while (saOff < saLen) { bool noneFound = true; if (Marshal.SizeOf <CdromXa>() + saOff <= saLen) { CdromXa xa = Marshal.ByteArrayToStructureBigEndian <CdromXa>(sa); if (xa.signature == XA_MAGIC) { xaExtensions = true; saOff += Marshal.SizeOf <CdromXa>(); noneFound = false; } } if (saOff + 2 >= saLen) { break; } ushort nextSignature = BigEndianBitConverter.ToUInt16(sa, saOff); switch (nextSignature) { // Easy, contains size field case APPLE_MAGIC: apple = true; saOff += sa[saOff + 2]; noneFound = false; break; // Not easy, contains size field case APPLE_MAGIC_OLD: apple = true; var appleId = (AppleOldId)sa[saOff + 2]; noneFound = false; switch (appleId) { case AppleOldId.ProDOS: saOff += Marshal.SizeOf <AppleProDOSOldSystemUse>(); break; case AppleOldId.TypeCreator: case AppleOldId.TypeCreatorBundle: saOff += Marshal.SizeOf <AppleHFSTypeCreatorSystemUse>(); break; case AppleOldId.TypeCreatorIcon: case AppleOldId.TypeCreatorIconBundle: saOff += Marshal.SizeOf <AppleHFSIconSystemUse>(); break; case AppleOldId.HFS: saOff += Marshal.SizeOf <AppleHFSOldSystemUse>(); break; } break; // IEEE-P1281 aka SUSP 1.12 case SUSP_INDICATOR: susp = true; saOff += sa[saOff + 2]; noneFound = false; while (saOff + 2 < saLen) { nextSignature = BigEndianBitConverter.ToUInt16(sa, saOff); switch (nextSignature) { case APPLE_MAGIC: if (sa[saOff + 3] == 1 && sa[saOff + 2] == 7) { apple = true; } else { apple |= sa[saOff + 3] != 1; } break; case SUSP_CONTINUATION when saOff + sa[saOff + 2] <= saLen: byte[] ce = new byte[sa[saOff + 2]]; Array.Copy(sa, saOff, ce, 0, ce.Length); ContinuationArea ca = Marshal.ByteArrayToStructureBigEndian <ContinuationArea>(ce); contareas.Add(ca); break; case SUSP_REFERENCE when saOff + sa[saOff + 2] <= saLen: byte[] er = new byte[sa[saOff + 2]]; Array.Copy(sa, saOff, er, 0, er.Length); refareas.Add(er); break; } rrip |= nextSignature == RRIP_MAGIC || nextSignature == RRIP_POSIX_ATTRIBUTES || nextSignature == RRIP_POSIX_DEV_NO || nextSignature == RRIP_SYMLINK || nextSignature == RRIP_NAME || nextSignature == RRIP_CHILDLINK || nextSignature == RRIP_PARENTLINK || nextSignature == RRIP_RELOCATED_DIR || nextSignature == RRIP_TIMESTAMPS || nextSignature == RRIP_SPARSE; ziso |= nextSignature == ZISO_MAGIC; amiga |= nextSignature == AMIGA_MAGIC; aaip |= nextSignature == AAIP_MAGIC || (nextSignature == AAIP_MAGIC_OLD && sa[saOff + 3] == 1 && sa[saOff + 2] >= 9); saOff += sa[saOff + 2]; if (nextSignature == SUSP_TERMINATOR) { break; } } break; } if (noneFound) { break; } } } rootOff += record.length; if (record.length == 0) { break; } } foreach (ContinuationArea ca in contareas) { uint caLen = (ca.ca_length_be + ca.offset_be) / (highSierraInfo ? hsvd.Value.logical_block_size : pvd.Value.logical_block_size); if ((ca.ca_length_be + ca.offset_be) % (highSierraInfo ? hsvd.Value.logical_block_size : pvd.Value.logical_block_size) > 0) { caLen++; } byte[] caSectors = imagePlugin.ReadSectors(ca.block_be, caLen); byte[] caData = new byte[ca.ca_length_be]; Array.Copy(caSectors, ca.offset_be, caData, 0, ca.ca_length_be); int caOff = 0; while (caOff < ca.ca_length_be) { ushort nextSignature = BigEndianBitConverter.ToUInt16(caData, caOff); switch (nextSignature) { // Apple never said to include its extensions inside a continuation area, but just in case case APPLE_MAGIC: if (caData[caOff + 3] == 1 && caData[caOff + 2] == 7) { apple = true; } else { apple |= caData[caOff + 3] != 1; } break; case SUSP_REFERENCE when caOff + caData[caOff + 2] <= ca.ca_length_be: byte[] er = new byte[caData[caOff + 2]]; Array.Copy(caData, caOff, er, 0, er.Length); refareas.Add(er); break; } rrip |= nextSignature == RRIP_MAGIC || nextSignature == RRIP_POSIX_ATTRIBUTES || nextSignature == RRIP_POSIX_DEV_NO || nextSignature == RRIP_SYMLINK || nextSignature == RRIP_NAME || nextSignature == RRIP_CHILDLINK || nextSignature == RRIP_PARENTLINK || nextSignature == RRIP_RELOCATED_DIR || nextSignature == RRIP_TIMESTAMPS || nextSignature == RRIP_SPARSE; ziso |= nextSignature == ZISO_MAGIC; amiga |= nextSignature == AMIGA_MAGIC; aaip |= nextSignature == AAIP_MAGIC || (nextSignature == AAIP_MAGIC_OLD && caData[caOff + 3] == 1 && caData[caOff + 2] >= 9); caOff += caData[caOff + 2]; } } if (refareas.Count > 0) { suspInformation.AppendLine("----------------------------------------"); suspInformation.AppendLine("SYSTEM USE SHARING PROTOCOL INFORMATION:"); suspInformation.AppendLine("----------------------------------------"); counter = 1; foreach (byte[] erb in refareas) { ReferenceArea er = Marshal.ByteArrayToStructureBigEndian <ReferenceArea>(erb); string extId = Encoding.GetString(erb, Marshal.SizeOf <ReferenceArea>(), er.id_len); string extDes = Encoding.GetString(erb, Marshal.SizeOf <ReferenceArea>() + er.id_len, er.des_len); string extSrc = Encoding.GetString(erb, Marshal.SizeOf <ReferenceArea>() + er.id_len + er.des_len, er.src_len); suspInformation.AppendFormat("Extension: {0}", counter).AppendLine(); suspInformation.AppendFormat("\tID: {0}, version {1}", extId, er.ext_ver).AppendLine(); suspInformation.AppendFormat("\tDescription: {0}", extDes).AppendLine(); suspInformation.AppendFormat("\tSource: {0}", extSrc).AppendLine(); counter++; } } byte[] ipbinSector = imagePlugin.ReadSector(0 + partition.Start); CD.IPBin? segaCd = CD.DecodeIPBin(ipbinSector); Saturn.IPBin? saturn = Saturn.DecodeIPBin(ipbinSector); Dreamcast.IPBin?dreamcast = Dreamcast.DecodeIPBin(ipbinSector); string fsFormat; if (highSierraInfo) { fsFormat = "High Sierra Format"; } else if (cdiInfo) { fsFormat = "CD-i"; } else { fsFormat = "ISO9660"; } isoMetadata.AppendFormat("{0} file system", fsFormat).AppendLine(); if (xaExtensions) { isoMetadata.AppendLine("CD-ROM XA extensions present."); } if (amiga) { isoMetadata.AppendLine("Amiga extensions present."); } if (apple) { isoMetadata.AppendLine("Apple extensions present."); } if (jolietvd != null) { isoMetadata.AppendLine("Joliet extensions present."); } if (susp) { isoMetadata.AppendLine("System Use Sharing Protocol present."); } if (rrip) { isoMetadata.AppendLine("Rock Ridge Interchange Protocol present."); } if (aaip) { isoMetadata.AppendLine("Arbitrary Attribute Interchange Protocol present."); } if (ziso) { isoMetadata.AppendLine("zisofs compression present."); } if (evd) { isoMetadata.AppendLine("Contains Enhanved Volume Descriptor."); } if (vpd) { isoMetadata.AppendLine("Contains Volume Partition Descriptor."); } if (bvd != null) { isoMetadata.AppendFormat("Disc bootable following {0} specifications.", bootSpec).AppendLine(); } if (segaCd != null) { isoMetadata.AppendLine("This is a SegaCD / MegaCD disc."); isoMetadata.AppendLine(CD.Prettify(segaCd)); } if (saturn != null) { isoMetadata.AppendLine("This is a Sega Saturn disc."); isoMetadata.AppendLine(Saturn.Prettify(saturn)); } if (dreamcast != null) { isoMetadata.AppendLine("This is a Sega Dreamcast disc."); isoMetadata.AppendLine(Dreamcast.Prettify(dreamcast)); } isoMetadata.AppendFormat("{0}------------------------------", cdiInfo ? "---------------" : ""). AppendLine(); isoMetadata.AppendFormat("{0}VOLUME DESCRIPTOR INFORMATION:", cdiInfo ? "FILE STRUCTURE " : ""). AppendLine(); isoMetadata.AppendFormat("{0}------------------------------", cdiInfo ? "---------------" : ""). AppendLine(); isoMetadata.AppendFormat("System identifier: {0}", decodedVd.SystemIdentifier).AppendLine(); isoMetadata.AppendFormat("Volume identifier: {0}", decodedVd.VolumeIdentifier).AppendLine(); isoMetadata.AppendFormat("Volume set identifier: {0}", decodedVd.VolumeSetIdentifier).AppendLine(); isoMetadata.AppendFormat("Publisher identifier: {0}", decodedVd.PublisherIdentifier).AppendLine(); isoMetadata.AppendFormat("Data preparer identifier: {0}", decodedVd.DataPreparerIdentifier).AppendLine(); isoMetadata.AppendFormat("Application identifier: {0}", decodedVd.ApplicationIdentifier).AppendLine(); isoMetadata.AppendFormat("Volume creation date: {0}", decodedVd.CreationTime).AppendLine(); if (decodedVd.HasModificationTime) { isoMetadata.AppendFormat("Volume modification date: {0}", decodedVd.ModificationTime).AppendLine(); } else { isoMetadata.AppendFormat("Volume has not been modified.").AppendLine(); } if (decodedVd.HasExpirationTime) { isoMetadata.AppendFormat("Volume expiration date: {0}", decodedVd.ExpirationTime).AppendLine(); } else { isoMetadata.AppendFormat("Volume does not expire.").AppendLine(); } if (decodedVd.HasEffectiveTime) { isoMetadata.AppendFormat("Volume effective date: {0}", decodedVd.EffectiveTime).AppendLine(); } else { isoMetadata.AppendFormat("Volume has always been effective.").AppendLine(); } isoMetadata.AppendFormat("Volume has {0} blocks of {1} bytes each", decodedVd.Blocks, decodedVd.BlockSize). AppendLine(); if (jolietvd != null) { isoMetadata.AppendLine("-------------------------------------"); isoMetadata.AppendLine("JOLIET VOLUME DESCRIPTOR INFORMATION:"); isoMetadata.AppendLine("-------------------------------------"); isoMetadata.AppendFormat("System identifier: {0}", decodedJolietVd.SystemIdentifier).AppendLine(); isoMetadata.AppendFormat("Volume identifier: {0}", decodedJolietVd.VolumeIdentifier).AppendLine(); isoMetadata.AppendFormat("Volume set identifier: {0}", decodedJolietVd.VolumeSetIdentifier). AppendLine(); isoMetadata.AppendFormat("Publisher identifier: {0}", decodedJolietVd.PublisherIdentifier).AppendLine(); isoMetadata.AppendFormat("Data preparer identifier: {0}", decodedJolietVd.DataPreparerIdentifier). AppendLine(); isoMetadata.AppendFormat("Application identifier: {0}", decodedJolietVd.ApplicationIdentifier). AppendLine(); isoMetadata.AppendFormat("Volume creation date: {0}", decodedJolietVd.CreationTime).AppendLine(); if (decodedJolietVd.HasModificationTime) { isoMetadata.AppendFormat("Volume modification date: {0}", decodedJolietVd.ModificationTime). AppendLine(); } else { isoMetadata.AppendFormat("Volume has not been modified.").AppendLine(); } if (decodedJolietVd.HasExpirationTime) { isoMetadata.AppendFormat("Volume expiration date: {0}", decodedJolietVd.ExpirationTime). AppendLine(); } else { isoMetadata.AppendFormat("Volume does not expire.").AppendLine(); } if (decodedJolietVd.HasEffectiveTime) { isoMetadata.AppendFormat("Volume effective date: {0}", decodedJolietVd.EffectiveTime).AppendLine(); } else { isoMetadata.AppendFormat("Volume has always been effective.").AppendLine(); } } if (torito != null) { vdSector = imagePlugin.ReadSector(torito.Value.catalog_sector + partition.Start); int toritoOff = 0; if (vdSector[toritoOff] != 1) { goto exit_torito; } ElToritoValidationEntry valentry = Marshal.ByteArrayToStructureLittleEndian <ElToritoValidationEntry>(vdSector, toritoOff, EL_TORITO_ENTRY_SIZE); if (valentry.signature != EL_TORITO_MAGIC) { goto exit_torito; } toritoOff += EL_TORITO_ENTRY_SIZE; ElToritoInitialEntry initialEntry = Marshal.ByteArrayToStructureLittleEndian <ElToritoInitialEntry>(vdSector, toritoOff, EL_TORITO_ENTRY_SIZE); initialEntry.boot_type = (ElToritoEmulation)((byte)initialEntry.boot_type & 0xF); AaruConsole.DebugWriteLine("DEBUG (ISO9660 plugin)", "initialEntry.load_rba = {0}", initialEntry.load_rba); AaruConsole.DebugWriteLine("DEBUG (ISO9660 plugin)", "initialEntry.sector_count = {0}", initialEntry.sector_count); byte[] bootImage = (initialEntry.load_rba + partition.Start + initialEntry.sector_count) - 1 <= partition.End ? imagePlugin.ReadSectors(initialEntry.load_rba + partition.Start, initialEntry.sector_count) : null; isoMetadata.AppendLine("----------------------"); isoMetadata.AppendLine("EL TORITO INFORMATION:"); isoMetadata.AppendLine("----------------------"); isoMetadata.AppendLine("Initial entry:"); isoMetadata.AppendFormat("\tDeveloper ID: {0}", Encoding.GetString(valentry.developer_id)).AppendLine(); if (initialEntry.bootable == ElToritoIndicator.Bootable) { isoMetadata.AppendFormat("\tBootable on {0}", valentry.platform_id).AppendLine(); isoMetadata.AppendFormat("\tBootable image starts at sector {0} and runs for {1} sectors", initialEntry.load_rba, initialEntry.sector_count).AppendLine(); if (valentry.platform_id == ElToritoPlatform.x86) { isoMetadata.AppendFormat("\tBootable image will be loaded at segment {0:X4}h", initialEntry.load_seg == 0 ? 0x7C0 : initialEntry.load_seg). AppendLine(); } else { isoMetadata.AppendFormat("\tBootable image will be loaded at 0x{0:X8}", (uint)initialEntry.load_seg * 10).AppendLine(); } switch (initialEntry.boot_type) { case ElToritoEmulation.None: isoMetadata.AppendLine("\tImage uses no emulation"); break; case ElToritoEmulation.Md2hd: isoMetadata.AppendLine("\tImage emulates a 5.25\" high-density (MD2HD, 1.2Mb) floppy"); break; case ElToritoEmulation.Mf2hd: isoMetadata.AppendLine("\tImage emulates a 3.5\" high-density (MF2HD, 1.44Mb) floppy"); break; case ElToritoEmulation.Mf2ed: isoMetadata.AppendLine("\tImage emulates a 3.5\" extra-density (MF2ED, 2.88Mb) floppy"); break; default: isoMetadata.AppendFormat("\tImage uses unknown emulation type {0}", (byte)initialEntry.boot_type).AppendLine(); break; } isoMetadata.AppendFormat("\tSystem type: 0x{0:X2}", initialEntry.system_type).AppendLine(); if (bootImage != null) { isoMetadata.AppendFormat("\tBootable image's SHA1: {0}", Sha1Context.Data(bootImage, out _)). AppendLine(); } } else { isoMetadata.AppendLine("\tNot bootable"); } toritoOff += EL_TORITO_ENTRY_SIZE; const int SECTION_COUNTER = 2; while (toritoOff < vdSector.Length && (vdSector[toritoOff] == (byte)ElToritoIndicator.Header || vdSector[toritoOff] == (byte)ElToritoIndicator.LastHeader)) { ElToritoSectionHeaderEntry sectionHeader = Marshal.ByteArrayToStructureLittleEndian <ElToritoSectionHeaderEntry>(vdSector, toritoOff, EL_TORITO_ENTRY_SIZE); toritoOff += EL_TORITO_ENTRY_SIZE; isoMetadata.AppendFormat("Boot section {0}:", SECTION_COUNTER); isoMetadata.AppendFormat("\tSection ID: {0}", Encoding.GetString(sectionHeader.identifier)). AppendLine(); for (int entryCounter = 1; entryCounter <= sectionHeader.entries && toritoOff < vdSector.Length; entryCounter++) { ElToritoSectionEntry sectionEntry = Marshal.ByteArrayToStructureLittleEndian <ElToritoSectionEntry>(vdSector, toritoOff, EL_TORITO_ENTRY_SIZE); toritoOff += EL_TORITO_ENTRY_SIZE; isoMetadata.AppendFormat("\tEntry {0}:", entryCounter); if (sectionEntry.bootable == ElToritoIndicator.Bootable) { bootImage = (sectionEntry.load_rba + partition.Start + sectionEntry.sector_count) - 1 <= partition.End ? imagePlugin.ReadSectors(sectionEntry.load_rba + partition.Start, sectionEntry.sector_count) : null; isoMetadata.AppendFormat("\t\tBootable on {0}", sectionHeader.platform_id).AppendLine(); isoMetadata.AppendFormat("\t\tBootable image starts at sector {0} and runs for {1} sectors", sectionEntry.load_rba, sectionEntry.sector_count).AppendLine(); if (valentry.platform_id == ElToritoPlatform.x86) { isoMetadata.AppendFormat("\t\tBootable image will be loaded at segment {0:X4}h", sectionEntry.load_seg == 0 ? 0x7C0 : sectionEntry.load_seg). AppendLine(); } else { isoMetadata.AppendFormat("\t\tBootable image will be loaded at 0x{0:X8}", (uint)sectionEntry.load_seg * 10).AppendLine(); } switch ((ElToritoEmulation)((byte)sectionEntry.boot_type & 0xF)) { case ElToritoEmulation.None: isoMetadata.AppendLine("\t\tImage uses no emulation"); break; case ElToritoEmulation.Md2hd: isoMetadata. AppendLine("\t\tImage emulates a 5.25\" high-density (MD2HD, 1.2Mb) floppy"); break; case ElToritoEmulation.Mf2hd: isoMetadata. AppendLine("\t\tImage emulates a 3.5\" high-density (MF2HD, 1.44Mb) floppy"); break; case ElToritoEmulation.Mf2ed: isoMetadata. AppendLine("\t\tImage emulates a 3.5\" extra-density (MF2ED, 2.88Mb) floppy"); break; default: isoMetadata.AppendFormat("\t\tImage uses unknown emulation type {0}", (byte)initialEntry.boot_type).AppendLine(); break; } isoMetadata.AppendFormat("\t\tSelection criteria type: {0}", sectionEntry.selection_criteria_type).AppendLine(); isoMetadata.AppendFormat("\t\tSystem type: 0x{0:X2}", sectionEntry.system_type). AppendLine(); if (bootImage != null) { isoMetadata.AppendFormat("\t\tBootable image's SHA1: {0}", Sha1Context.Data(bootImage, out _)).AppendLine(); } } else { isoMetadata.AppendLine("\t\tNot bootable"); } var flags = (ElToritoFlags)((byte)sectionEntry.boot_type & 0xF0); if (flags.HasFlag(ElToritoFlags.ATAPI)) { isoMetadata.AppendLine("\t\tImage contains ATAPI drivers"); } if (flags.HasFlag(ElToritoFlags.SCSI)) { isoMetadata.AppendLine("\t\tImage contains SCSI drivers"); } if (!flags.HasFlag(ElToritoFlags.Continued)) { continue; } while (toritoOff < vdSector.Length) { ElToritoSectionEntryExtension sectionExtension = Marshal.ByteArrayToStructureLittleEndian <ElToritoSectionEntryExtension>(vdSector, toritoOff, EL_TORITO_ENTRY_SIZE); toritoOff += EL_TORITO_ENTRY_SIZE; if (!sectionExtension.extension_flags.HasFlag(ElToritoFlags.Continued)) { break; } } } if (sectionHeader.header_id == ElToritoIndicator.LastHeader) { break; } } } exit_torito: if (refareas.Count > 0) { isoMetadata.Append(suspInformation); } XmlFsType.Type = fsFormat; if (jolietvd != null) { XmlFsType.VolumeName = decodedJolietVd.VolumeIdentifier; if (string.IsNullOrEmpty(decodedJolietVd.SystemIdentifier) || decodedVd.SystemIdentifier.Length > decodedJolietVd.SystemIdentifier.Length) { XmlFsType.SystemIdentifier = decodedVd.SystemIdentifier; } else { XmlFsType.SystemIdentifier = string.IsNullOrEmpty(decodedJolietVd.SystemIdentifier) ? null : decodedJolietVd.SystemIdentifier; } if (string.IsNullOrEmpty(decodedJolietVd.VolumeSetIdentifier) || decodedVd.VolumeSetIdentifier.Length > decodedJolietVd.VolumeSetIdentifier.Length) { XmlFsType.VolumeSetIdentifier = decodedVd.VolumeSetIdentifier; } else { XmlFsType.VolumeSetIdentifier = string.IsNullOrEmpty(decodedJolietVd.VolumeSetIdentifier) ? null : decodedJolietVd.VolumeSetIdentifier; } if (string.IsNullOrEmpty(decodedJolietVd.PublisherIdentifier) || decodedVd.PublisherIdentifier.Length > decodedJolietVd.PublisherIdentifier.Length) { XmlFsType.PublisherIdentifier = decodedVd.PublisherIdentifier; } else { XmlFsType.PublisherIdentifier = string.IsNullOrEmpty(decodedJolietVd.PublisherIdentifier) ? null : decodedJolietVd.PublisherIdentifier; } if (string.IsNullOrEmpty(decodedJolietVd.DataPreparerIdentifier) || decodedVd.DataPreparerIdentifier.Length > decodedJolietVd.DataPreparerIdentifier.Length) { XmlFsType.DataPreparerIdentifier = decodedVd.DataPreparerIdentifier; } else { XmlFsType.DataPreparerIdentifier = string.IsNullOrEmpty(decodedJolietVd.DataPreparerIdentifier) ? null : decodedJolietVd.DataPreparerIdentifier; } if (string.IsNullOrEmpty(decodedJolietVd.ApplicationIdentifier) || decodedVd.ApplicationIdentifier.Length > decodedJolietVd.ApplicationIdentifier.Length) { XmlFsType.ApplicationIdentifier = decodedVd.ApplicationIdentifier; } else { XmlFsType.ApplicationIdentifier = string.IsNullOrEmpty(decodedJolietVd.ApplicationIdentifier) ? null : decodedJolietVd.ApplicationIdentifier; } XmlFsType.CreationDate = decodedJolietVd.CreationTime; XmlFsType.CreationDateSpecified = true; if (decodedJolietVd.HasModificationTime) { XmlFsType.ModificationDate = decodedJolietVd.ModificationTime; XmlFsType.ModificationDateSpecified = true; } if (decodedJolietVd.HasExpirationTime) { XmlFsType.ExpirationDate = decodedJolietVd.ExpirationTime; XmlFsType.ExpirationDateSpecified = true; } if (decodedJolietVd.HasEffectiveTime) { XmlFsType.EffectiveDate = decodedJolietVd.EffectiveTime; XmlFsType.EffectiveDateSpecified = true; } } else { XmlFsType.SystemIdentifier = decodedVd.SystemIdentifier; XmlFsType.VolumeName = decodedVd.VolumeIdentifier; XmlFsType.VolumeSetIdentifier = decodedVd.VolumeSetIdentifier; XmlFsType.PublisherIdentifier = decodedVd.PublisherIdentifier; XmlFsType.DataPreparerIdentifier = decodedVd.DataPreparerIdentifier; XmlFsType.ApplicationIdentifier = decodedVd.ApplicationIdentifier; XmlFsType.CreationDate = decodedVd.CreationTime; XmlFsType.CreationDateSpecified = true; if (decodedVd.HasModificationTime) { XmlFsType.ModificationDate = decodedVd.ModificationTime; XmlFsType.ModificationDateSpecified = true; } if (decodedVd.HasExpirationTime) { XmlFsType.ExpirationDate = decodedVd.ExpirationTime; XmlFsType.ExpirationDateSpecified = true; } if (decodedVd.HasEffectiveTime) { XmlFsType.EffectiveDate = decodedVd.EffectiveTime; XmlFsType.EffectiveDateSpecified = true; } } XmlFsType.Bootable |= bvd != null || segaCd != null || saturn != null || dreamcast != null; XmlFsType.Clusters = decodedVd.Blocks; XmlFsType.ClusterSize = decodedVd.BlockSize; information = isoMetadata.ToString(); }
public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, Encoding encoding) { Encoding = encoding ?? Encoding.GetEncoding("iso-8859-15"); information = ""; StringBuilder sb = new StringBuilder(); BigEndianBitConverter.IsLittleEndian = true; // Start in little endian until we know what are we handling here int start = 0; bool xenix = false; bool sysv = false; bool sys7th = false; bool coherent = false; bool xenix3 = false; byte[] sb_sector; byte sb_size_in_sectors; int offset = 0; if (imagePlugin.Info.SectorSize <= 0x400 ) // Check if underlying device sector size is smaller than SuperBlock size { sb_size_in_sectors = (byte)(0x400 / imagePlugin.Info.SectorSize); } else { sb_size_in_sectors = 1; // If not a single sector can store it } // Sectors in a cylinder int spc = (int)(imagePlugin.Info.Heads * imagePlugin.Info.SectorsPerTrack); // Superblock can start on 0x000, 0x200, 0x600 and 0x800, not aligned, so we assume 16 (128 bytes/sector) sectors as a safe value int[] locations = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, // Superblock can also skip one cylinder (for boot) spc }; foreach (int i in locations) { sb_sector = imagePlugin.ReadSectors((ulong)i + partition.Start, sb_size_in_sectors); uint magic = BigEndianBitConverter.ToUInt32(sb_sector, 0x3F8); if (magic == XENIX_MAGIC || magic == SYSV_MAGIC) { BigEndianBitConverter.IsLittleEndian = true; // Little endian if (magic == SYSV_MAGIC) { sysv = true; offset = 0x200; } else { xenix = true; } start = i; break; } if (magic == XENIX_CIGAM || magic == SYSV_CIGAM) { BigEndianBitConverter.IsLittleEndian = false; // Big endian if (magic == SYSV_CIGAM) { sysv = true; offset = 0x200; } else { xenix = true; } start = i; break; } magic = BigEndianBitConverter.ToUInt32(sb_sector, 0x1F0); // XENIX 3 magic location if (magic == XENIX_MAGIC) { BigEndianBitConverter.IsLittleEndian = true; // Little endian xenix3 = true; start = i; break; } if (magic == XENIX_CIGAM) { BigEndianBitConverter.IsLittleEndian = false; // Big endian xenix3 = true; start = i; break; } magic = BigEndianBitConverter.ToUInt32(sb_sector, 0x1F8); // XENIX magic location if (magic == SYSV_MAGIC) { BigEndianBitConverter.IsLittleEndian = true; // Little endian sysv = true; start = i; break; } if (magic == SYSV_CIGAM) { BigEndianBitConverter.IsLittleEndian = false; // Big endian sysv = true; start = i; break; } byte[] coherent_string = new byte[6]; Array.Copy(sb_sector, 0x1E4, coherent_string, 0, 6); // Coherent UNIX s_fname location string s_fname = StringHandlers.CToString(coherent_string, Encoding); Array.Copy(sb_sector, 0x1EA, coherent_string, 0, 6); // Coherent UNIX s_fpack location string s_fpack = StringHandlers.CToString(coherent_string, Encoding); if (s_fname == COH_FNAME && s_fpack == COH_FPACK || s_fname == COH_XXXXX && s_fpack == COH_XXXXX || s_fname == COH_XXXXS && s_fpack == COH_XXXXN) { BigEndianBitConverter.IsLittleEndian = true; // Coherent is in PDP endianness, use helper for that coherent = true; start = i; break; } // Now try to identify 7th edition uint s_fsize = BitConverter.ToUInt32(sb_sector, 0x002); ushort s_nfree = BitConverter.ToUInt16(sb_sector, 0x006); ushort s_ninode = BitConverter.ToUInt16(sb_sector, 0x0D0); if (s_fsize <= 0 || s_fsize >= 0xFFFFFFFF || s_nfree <= 0 || s_nfree >= 0xFFFF || s_ninode <= 0 || s_ninode >= 0xFFFF) { continue; } if ((s_fsize & 0xFF) == 0x00 && (s_nfree & 0xFF) == 0x00 && (s_ninode & 0xFF) == 0x00) { // Byteswap s_fsize = ((s_fsize & 0xFF) << 24) + ((s_fsize & 0xFF00) << 8) + ((s_fsize & 0xFF0000) >> 8) + ((s_fsize & 0xFF000000) >> 24); s_nfree = (ushort)(s_nfree >> 8); s_ninode = (ushort)(s_ninode >> 8); } if ((s_fsize & 0xFF000000) != 0x00 || (s_nfree & 0xFF00) != 0x00 || (s_ninode & 0xFF00) != 0x00) { continue; } if (s_fsize >= V7_MAXSIZE || s_nfree >= V7_NICFREE || s_ninode >= V7_NICINOD) { continue; } if (s_fsize * 1024 != (partition.End - partition.Start) * imagePlugin.Info.SectorSize && s_fsize * 512 != (partition.End - partition.Start) * imagePlugin.Info.SectorSize) { continue; } sys7th = true; BigEndianBitConverter.IsLittleEndian = true; start = i; break; } if (!sys7th && !sysv && !coherent && !xenix && !xenix3) { return; } XmlFsType = new FileSystemType(); if (xenix || xenix3) { byte[] xenix_strings = new byte[6]; XenixSuperBlock xnx_sb = new XenixSuperBlock(); sb_sector = imagePlugin.ReadSectors((ulong)start + partition.Start, sb_size_in_sectors); if (xenix3) { xnx_sb.s_isize = BigEndianBitConverter.ToUInt16(sb_sector, 0x000); xnx_sb.s_fsize = BigEndianBitConverter.ToUInt32(sb_sector, 0x002); xnx_sb.s_nfree = BigEndianBitConverter.ToUInt16(sb_sector, 0x006); xnx_sb.s_ninode = BigEndianBitConverter.ToUInt16(sb_sector, 0x0D0); xnx_sb.s_flock = sb_sector[0x19A]; xnx_sb.s_ilock = sb_sector[0x19B]; xnx_sb.s_fmod = sb_sector[0x19C]; xnx_sb.s_ronly = sb_sector[0x19D]; xnx_sb.s_time = BigEndianBitConverter.ToInt32(sb_sector, 0x19E); xnx_sb.s_tfree = BigEndianBitConverter.ToUInt32(sb_sector, 0x1A2); xnx_sb.s_tinode = BigEndianBitConverter.ToUInt16(sb_sector, 0x1A6); xnx_sb.s_cylblks = BigEndianBitConverter.ToUInt16(sb_sector, 0x1A8); xnx_sb.s_gapblks = BigEndianBitConverter.ToUInt16(sb_sector, 0x1AA); xnx_sb.s_dinfo0 = BigEndianBitConverter.ToUInt16(sb_sector, 0x1AC); xnx_sb.s_dinfo1 = BigEndianBitConverter.ToUInt16(sb_sector, 0x1AE); Array.Copy(sb_sector, 0x1B0, xenix_strings, 0, 6); xnx_sb.s_fname = StringHandlers.CToString(xenix_strings, Encoding); Array.Copy(sb_sector, 0x1B6, xenix_strings, 0, 6); xnx_sb.s_fpack = StringHandlers.CToString(xenix_strings, Encoding); xnx_sb.s_clean = sb_sector[0x1BC]; xnx_sb.s_magic = BigEndianBitConverter.ToUInt32(sb_sector, 0x1F0); xnx_sb.s_type = BigEndianBitConverter.ToUInt32(sb_sector, 0x1F4); } else { xnx_sb.s_isize = BigEndianBitConverter.ToUInt16(sb_sector, 0x000); xnx_sb.s_fsize = BigEndianBitConverter.ToUInt32(sb_sector, 0x002); xnx_sb.s_nfree = BigEndianBitConverter.ToUInt16(sb_sector, 0x006); xnx_sb.s_ninode = BigEndianBitConverter.ToUInt16(sb_sector, 0x198); xnx_sb.s_flock = sb_sector[0x262]; xnx_sb.s_ilock = sb_sector[0x263]; xnx_sb.s_fmod = sb_sector[0x264]; xnx_sb.s_ronly = sb_sector[0x265]; xnx_sb.s_time = BigEndianBitConverter.ToInt32(sb_sector, 0x266); xnx_sb.s_tfree = BigEndianBitConverter.ToUInt32(sb_sector, 0x26A); xnx_sb.s_tinode = BigEndianBitConverter.ToUInt16(sb_sector, 0x26E); xnx_sb.s_cylblks = BigEndianBitConverter.ToUInt16(sb_sector, 0x270); xnx_sb.s_gapblks = BigEndianBitConverter.ToUInt16(sb_sector, 0x272); xnx_sb.s_dinfo0 = BigEndianBitConverter.ToUInt16(sb_sector, 0x274); xnx_sb.s_dinfo1 = BigEndianBitConverter.ToUInt16(sb_sector, 0x276); Array.Copy(sb_sector, 0x278, xenix_strings, 0, 6); xnx_sb.s_fname = StringHandlers.CToString(xenix_strings, Encoding); Array.Copy(sb_sector, 0x27E, xenix_strings, 0, 6); xnx_sb.s_fpack = StringHandlers.CToString(xenix_strings, Encoding); xnx_sb.s_clean = sb_sector[0x284]; xnx_sb.s_magic = BigEndianBitConverter.ToUInt32(sb_sector, 0x3F8); xnx_sb.s_type = BigEndianBitConverter.ToUInt32(sb_sector, 0x3FC); } uint bs = 512; sb.AppendLine("XENIX filesystem"); XmlFsType.Type = "XENIX fs"; switch (xnx_sb.s_type) { case 1: sb.AppendLine("512 bytes per block"); XmlFsType.ClusterSize = 512; break; case 2: sb.AppendLine("1024 bytes per block"); bs = 1024; XmlFsType.ClusterSize = 1024; break; case 3: sb.AppendLine("2048 bytes per block"); bs = 2048; XmlFsType.ClusterSize = 2048; break; default: sb.AppendFormat("Unknown s_type value: 0x{0:X8}", xnx_sb.s_type).AppendLine(); break; } if (imagePlugin.Info.SectorSize == 2336 || imagePlugin.Info.SectorSize == 2352 || imagePlugin.Info.SectorSize == 2448) { if (bs != 2048) { sb .AppendFormat("WARNING: Filesystem indicates {0} bytes/block while device indicates {1} bytes/sector", bs, 2048).AppendLine(); } } else { if (bs != imagePlugin.Info.SectorSize) { sb .AppendFormat("WARNING: Filesystem indicates {0} bytes/block while device indicates {1} bytes/sector", bs, imagePlugin.Info.SectorSize).AppendLine(); } } sb.AppendFormat("{0} zones on volume ({1} bytes)", xnx_sb.s_fsize, xnx_sb.s_fsize * bs).AppendLine(); sb.AppendFormat("{0} free zones on volume ({1} bytes)", xnx_sb.s_tfree, xnx_sb.s_tfree * bs) .AppendLine(); sb.AppendFormat("{0} free blocks on list ({1} bytes)", xnx_sb.s_nfree, xnx_sb.s_nfree * bs) .AppendLine(); sb.AppendFormat("{0} blocks per cylinder ({1} bytes)", xnx_sb.s_cylblks, xnx_sb.s_cylblks * bs) .AppendLine(); sb.AppendFormat("{0} blocks per gap ({1} bytes)", xnx_sb.s_gapblks, xnx_sb.s_gapblks * bs).AppendLine(); sb.AppendFormat("First data zone: {0}", xnx_sb.s_isize).AppendLine(); sb.AppendFormat("{0} free inodes on volume", xnx_sb.s_tinode).AppendLine(); sb.AppendFormat("{0} free inodes on list", xnx_sb.s_ninode).AppendLine(); if (xnx_sb.s_flock > 0) { sb.AppendLine("Free block list is locked"); } if (xnx_sb.s_ilock > 0) { sb.AppendLine("inode cache is locked"); } if (xnx_sb.s_fmod > 0) { sb.AppendLine("Superblock is being modified"); } if (xnx_sb.s_ronly > 0) { sb.AppendLine("Volume is mounted read-only"); } sb.AppendFormat("Superblock last updated on {0}", DateHandlers.UnixToDateTime(xnx_sb.s_time)) .AppendLine(); if (xnx_sb.s_time != 0) { XmlFsType.ModificationDate = DateHandlers.UnixToDateTime(xnx_sb.s_time); XmlFsType.ModificationDateSpecified = true; } sb.AppendFormat("Volume name: {0}", xnx_sb.s_fname).AppendLine(); XmlFsType.VolumeName = xnx_sb.s_fname; sb.AppendFormat("Pack name: {0}", xnx_sb.s_fpack).AppendLine(); if (xnx_sb.s_clean == 0x46) { sb.AppendLine("Volume is clean"); } else { sb.AppendLine("Volume is dirty"); XmlFsType.Dirty = true; } } if (sysv) { sb_sector = imagePlugin.ReadSectors((ulong)start + partition.Start, sb_size_in_sectors); byte[] sysv_strings = new byte[6]; SystemVRelease4SuperBlock sysv_sb = new SystemVRelease4SuperBlock { s_type = BigEndianBitConverter.ToUInt32(sb_sector, 0x1FC + offset) }; uint bs = 512; switch (sysv_sb.s_type) { case 1: XmlFsType.ClusterSize = 512; break; case 2: bs = 1024; XmlFsType.ClusterSize = 1024; break; case 3: bs = 2048; XmlFsType.ClusterSize = 2048; break; default: sb.AppendFormat("Unknown s_type value: 0x{0:X8}", sysv_sb.s_type).AppendLine(); break; } sysv_sb.s_fsize = BigEndianBitConverter.ToUInt32(sb_sector, 0x002 + offset); bool sysvr4 = sysv_sb.s_fsize * bs <= 0 || sysv_sb.s_fsize * bs != partition.Size; if (sysvr4) { sysv_sb.s_isize = BigEndianBitConverter.ToUInt16(sb_sector, 0x000 + offset); sysv_sb.s_state = BigEndianBitConverter.ToUInt32(sb_sector, 0x1F4 + offset); sysv_sb.s_magic = BigEndianBitConverter.ToUInt32(sb_sector, 0x1F8 + offset); sysv_sb.s_fsize = BigEndianBitConverter.ToUInt32(sb_sector, 0x004 + offset); sysv_sb.s_nfree = BigEndianBitConverter.ToUInt16(sb_sector, 0x008 + offset); sysv_sb.s_ninode = BigEndianBitConverter.ToUInt16(sb_sector, 0x0D4 + offset); sysv_sb.s_flock = sb_sector[0x1A0 + offset]; sysv_sb.s_ilock = sb_sector[0x1A1 + offset]; sysv_sb.s_fmod = sb_sector[0x1A2 + offset]; sysv_sb.s_ronly = sb_sector[0x1A3 + offset]; sysv_sb.s_time = BigEndianBitConverter.ToUInt32(sb_sector, 0x1A4 + offset); sysv_sb.s_cylblks = BigEndianBitConverter.ToUInt16(sb_sector, 0x1A8 + offset); sysv_sb.s_gapblks = BigEndianBitConverter.ToUInt16(sb_sector, 0x1AA + offset); sysv_sb.s_dinfo0 = BigEndianBitConverter.ToUInt16(sb_sector, 0x1AC + offset); sysv_sb.s_dinfo1 = BigEndianBitConverter.ToUInt16(sb_sector, 0x1AE + offset); sysv_sb.s_tfree = BigEndianBitConverter.ToUInt32(sb_sector, 0x1B0 + offset); sysv_sb.s_tinode = BigEndianBitConverter.ToUInt16(sb_sector, 0x1B4 + offset); Array.Copy(sb_sector, 0x1B6 + offset, sysv_strings, 0, 6); sysv_sb.s_fname = StringHandlers.CToString(sysv_strings, Encoding); Array.Copy(sb_sector, 0x1BC + offset, sysv_strings, 0, 6); sysv_sb.s_fpack = StringHandlers.CToString(sysv_strings, Encoding); sb.AppendLine("System V Release 4 filesystem"); XmlFsType.Type = "SVR4 fs"; } else { sysv_sb.s_isize = BigEndianBitConverter.ToUInt16(sb_sector, 0x000 + offset); sysv_sb.s_state = BigEndianBitConverter.ToUInt32(sb_sector, 0x1F4 + offset); sysv_sb.s_magic = BigEndianBitConverter.ToUInt32(sb_sector, 0x1F8 + offset); sysv_sb.s_fsize = BigEndianBitConverter.ToUInt32(sb_sector, 0x002 + offset); sysv_sb.s_nfree = BigEndianBitConverter.ToUInt16(sb_sector, 0x006 + offset); sysv_sb.s_ninode = BigEndianBitConverter.ToUInt16(sb_sector, 0x0D0 + offset); sysv_sb.s_flock = sb_sector[0x19A + offset]; sysv_sb.s_ilock = sb_sector[0x19B + offset]; sysv_sb.s_fmod = sb_sector[0x19C + offset]; sysv_sb.s_ronly = sb_sector[0x19D + offset]; sysv_sb.s_time = BigEndianBitConverter.ToUInt32(sb_sector, 0x19E + offset); sysv_sb.s_cylblks = BigEndianBitConverter.ToUInt16(sb_sector, 0x1A2 + offset); sysv_sb.s_gapblks = BigEndianBitConverter.ToUInt16(sb_sector, 0x1A4 + offset); sysv_sb.s_dinfo0 = BigEndianBitConverter.ToUInt16(sb_sector, 0x1A6 + offset); sysv_sb.s_dinfo1 = BigEndianBitConverter.ToUInt16(sb_sector, 0x1A8 + offset); sysv_sb.s_tfree = BigEndianBitConverter.ToUInt32(sb_sector, 0x1AA + offset); sysv_sb.s_tinode = BigEndianBitConverter.ToUInt16(sb_sector, 0x1AE + offset); Array.Copy(sb_sector, 0x1B0 + offset, sysv_strings, 0, 6); sysv_sb.s_fname = StringHandlers.CToString(sysv_strings, Encoding); Array.Copy(sb_sector, 0x1B6 + offset, sysv_strings, 0, 6); sysv_sb.s_fpack = StringHandlers.CToString(sysv_strings, Encoding); sb.AppendLine("System V Release 2 filesystem"); XmlFsType.Type = "SVR2 fs"; } sb.AppendFormat("{0} bytes per block", bs).AppendLine(); XmlFsType.Clusters = sysv_sb.s_fsize; sb.AppendFormat("{0} zones on volume ({1} bytes)", sysv_sb.s_fsize, sysv_sb.s_fsize * bs).AppendLine(); sb.AppendFormat("{0} free zones on volume ({1} bytes)", sysv_sb.s_tfree, sysv_sb.s_tfree * bs) .AppendLine(); sb.AppendFormat("{0} free blocks on list ({1} bytes)", sysv_sb.s_nfree, sysv_sb.s_nfree * bs) .AppendLine(); sb.AppendFormat("{0} blocks per cylinder ({1} bytes)", sysv_sb.s_cylblks, sysv_sb.s_cylblks * bs) .AppendLine(); sb.AppendFormat("{0} blocks per gap ({1} bytes)", sysv_sb.s_gapblks, sysv_sb.s_gapblks * bs) .AppendLine(); sb.AppendFormat("First data zone: {0}", sysv_sb.s_isize).AppendLine(); sb.AppendFormat("{0} free inodes on volume", sysv_sb.s_tinode).AppendLine(); sb.AppendFormat("{0} free inodes on list", sysv_sb.s_ninode).AppendLine(); if (sysv_sb.s_flock > 0) { sb.AppendLine("Free block list is locked"); } if (sysv_sb.s_ilock > 0) { sb.AppendLine("inode cache is locked"); } if (sysv_sb.s_fmod > 0) { sb.AppendLine("Superblock is being modified"); } if (sysv_sb.s_ronly > 0) { sb.AppendLine("Volume is mounted read-only"); } sb.AppendFormat("Superblock last updated on {0}", DateHandlers.UnixUnsignedToDateTime(sysv_sb.s_time)) .AppendLine(); if (sysv_sb.s_time != 0) { XmlFsType.ModificationDate = DateHandlers.UnixUnsignedToDateTime(sysv_sb.s_time); XmlFsType.ModificationDateSpecified = true; } sb.AppendFormat("Volume name: {0}", sysv_sb.s_fname).AppendLine(); XmlFsType.VolumeName = sysv_sb.s_fname; sb.AppendFormat("Pack name: {0}", sysv_sb.s_fpack).AppendLine(); if (sysv_sb.s_state == 0x7C269D38 - sysv_sb.s_time) { sb.AppendLine("Volume is clean"); } else { sb.AppendLine("Volume is dirty"); XmlFsType.Dirty = true; } } if (coherent) { sb_sector = imagePlugin.ReadSectors((ulong)start + partition.Start, sb_size_in_sectors); CoherentSuperBlock coh_sb = new CoherentSuperBlock(); byte[] coh_strings = new byte[6]; coh_sb.s_isize = BitConverter.ToUInt16(sb_sector, 0x000); coh_sb.s_fsize = Swapping.PDPFromLittleEndian(BitConverter.ToUInt32(sb_sector, 0x002)); coh_sb.s_nfree = BitConverter.ToUInt16(sb_sector, 0x006); coh_sb.s_ninode = BitConverter.ToUInt16(sb_sector, 0x108); coh_sb.s_flock = sb_sector[0x1D2]; coh_sb.s_ilock = sb_sector[0x1D3]; coh_sb.s_fmod = sb_sector[0x1D4]; coh_sb.s_ronly = sb_sector[0x1D5]; coh_sb.s_time = Swapping.PDPFromLittleEndian(BitConverter.ToUInt32(sb_sector, 0x1D6)); coh_sb.s_tfree = Swapping.PDPFromLittleEndian(BitConverter.ToUInt32(sb_sector, 0x1DA)); coh_sb.s_tinode = BitConverter.ToUInt16(sb_sector, 0x1DE); coh_sb.s_int_m = BitConverter.ToUInt16(sb_sector, 0x1E0); coh_sb.s_int_n = BitConverter.ToUInt16(sb_sector, 0x1E2); Array.Copy(sb_sector, 0x1E4, coh_strings, 0, 6); coh_sb.s_fname = StringHandlers.CToString(coh_strings, Encoding); Array.Copy(sb_sector, 0x1EA, coh_strings, 0, 6); coh_sb.s_fpack = StringHandlers.CToString(coh_strings, Encoding); XmlFsType.Type = "Coherent fs"; XmlFsType.ClusterSize = 512; XmlFsType.Clusters = coh_sb.s_fsize; sb.AppendLine("Coherent UNIX filesystem"); if (imagePlugin.Info.SectorSize != 512) { sb .AppendFormat("WARNING: Filesystem indicates {0} bytes/block while device indicates {1} bytes/sector", 512, 2048).AppendLine(); } sb.AppendFormat("{0} zones on volume ({1} bytes)", coh_sb.s_fsize, coh_sb.s_fsize * 512).AppendLine(); sb.AppendFormat("{0} free zones on volume ({1} bytes)", coh_sb.s_tfree, coh_sb.s_tfree * 512) .AppendLine(); sb.AppendFormat("{0} free blocks on list ({1} bytes)", coh_sb.s_nfree, coh_sb.s_nfree * 512) .AppendLine(); sb.AppendFormat("First data zone: {0}", coh_sb.s_isize).AppendLine(); sb.AppendFormat("{0} free inodes on volume", coh_sb.s_tinode).AppendLine(); sb.AppendFormat("{0} free inodes on list", coh_sb.s_ninode).AppendLine(); if (coh_sb.s_flock > 0) { sb.AppendLine("Free block list is locked"); } if (coh_sb.s_ilock > 0) { sb.AppendLine("inode cache is locked"); } if (coh_sb.s_fmod > 0) { sb.AppendLine("Superblock is being modified"); } if (coh_sb.s_ronly > 0) { sb.AppendLine("Volume is mounted read-only"); } sb.AppendFormat("Superblock last updated on {0}", DateHandlers.UnixUnsignedToDateTime(coh_sb.s_time)) .AppendLine(); if (coh_sb.s_time != 0) { XmlFsType.ModificationDate = DateHandlers.UnixUnsignedToDateTime(coh_sb.s_time); XmlFsType.ModificationDateSpecified = true; } sb.AppendFormat("Volume name: {0}", coh_sb.s_fname).AppendLine(); XmlFsType.VolumeName = coh_sb.s_fname; sb.AppendFormat("Pack name: {0}", coh_sb.s_fpack).AppendLine(); } if (sys7th) { sb_sector = imagePlugin.ReadSectors((ulong)start + partition.Start, sb_size_in_sectors); UNIX7thEditionSuperBlock v7_sb = new UNIX7thEditionSuperBlock(); byte[] sys7_strings = new byte[6]; v7_sb.s_isize = BigEndianBitConverter.ToUInt16(sb_sector, 0x000); v7_sb.s_fsize = BigEndianBitConverter.ToUInt32(sb_sector, 0x002); v7_sb.s_nfree = BigEndianBitConverter.ToUInt16(sb_sector, 0x006); v7_sb.s_ninode = BigEndianBitConverter.ToUInt16(sb_sector, 0x0D0); v7_sb.s_flock = sb_sector[0x19A]; v7_sb.s_ilock = sb_sector[0x19B]; v7_sb.s_fmod = sb_sector[0x19C]; v7_sb.s_ronly = sb_sector[0x19D]; v7_sb.s_time = BigEndianBitConverter.ToUInt32(sb_sector, 0x19E); v7_sb.s_tfree = BigEndianBitConverter.ToUInt32(sb_sector, 0x1A2); v7_sb.s_tinode = BigEndianBitConverter.ToUInt16(sb_sector, 0x1A6); v7_sb.s_int_m = BigEndianBitConverter.ToUInt16(sb_sector, 0x1A8); v7_sb.s_int_n = BigEndianBitConverter.ToUInt16(sb_sector, 0x1AA); Array.Copy(sb_sector, 0x1AC, sys7_strings, 0, 6); v7_sb.s_fname = StringHandlers.CToString(sys7_strings, Encoding); Array.Copy(sb_sector, 0x1B2, sys7_strings, 0, 6); v7_sb.s_fpack = StringHandlers.CToString(sys7_strings, Encoding); XmlFsType.Type = "UNIX 7th Edition fs"; XmlFsType.ClusterSize = 512; XmlFsType.Clusters = v7_sb.s_fsize; sb.AppendLine("UNIX 7th Edition filesystem"); if (imagePlugin.Info.SectorSize != 512) { sb .AppendFormat("WARNING: Filesystem indicates {0} bytes/block while device indicates {1} bytes/sector", 512, 2048).AppendLine(); } sb.AppendFormat("{0} zones on volume ({1} bytes)", v7_sb.s_fsize, v7_sb.s_fsize * 512).AppendLine(); sb.AppendFormat("{0} free zones on volume ({1} bytes)", v7_sb.s_tfree, v7_sb.s_tfree * 512) .AppendLine(); sb.AppendFormat("{0} free blocks on list ({1} bytes)", v7_sb.s_nfree, v7_sb.s_nfree * 512).AppendLine(); sb.AppendFormat("First data zone: {0}", v7_sb.s_isize).AppendLine(); sb.AppendFormat("{0} free inodes on volume", v7_sb.s_tinode).AppendLine(); sb.AppendFormat("{0} free inodes on list", v7_sb.s_ninode).AppendLine(); if (v7_sb.s_flock > 0) { sb.AppendLine("Free block list is locked"); } if (v7_sb.s_ilock > 0) { sb.AppendLine("inode cache is locked"); } if (v7_sb.s_fmod > 0) { sb.AppendLine("Superblock is being modified"); } if (v7_sb.s_ronly > 0) { sb.AppendLine("Volume is mounted read-only"); } sb.AppendFormat("Superblock last updated on {0}", DateHandlers.UnixUnsignedToDateTime(v7_sb.s_time)) .AppendLine(); if (v7_sb.s_time != 0) { XmlFsType.ModificationDate = DateHandlers.UnixUnsignedToDateTime(v7_sb.s_time); XmlFsType.ModificationDateSpecified = true; } sb.AppendFormat("Volume name: {0}", v7_sb.s_fname).AppendLine(); XmlFsType.VolumeName = v7_sb.s_fname; sb.AppendFormat("Pack name: {0}", v7_sb.s_fpack).AppendLine(); } information = sb.ToString(); BigEndianBitConverter.IsLittleEndian = false; // Return to default (bigendian) }
public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, Encoding encoding) { Encoding = encoding ?? Encoding.GetEncoding("iso-8859-15"); information = ""; var sb = new StringBuilder(); uint sectors = QNX6_SUPER_BLOCK_SIZE / imagePlugin.Info.SectorSize; uint bootSectors = QNX6_BOOT_BLOCKS_SIZE / imagePlugin.Info.SectorSize; byte[] audiSector = imagePlugin.ReadSectors(partition.Start, sectors); byte[] sector = imagePlugin.ReadSectors(partition.Start + bootSectors, sectors); if (sector.Length < QNX6_SUPER_BLOCK_SIZE) { return; } QNX6_AudiSuperBlock audiSb = Marshal.ByteArrayToStructureLittleEndian <QNX6_AudiSuperBlock>(audiSector); QNX6_SuperBlock qnxSb = Marshal.ByteArrayToStructureLittleEndian <QNX6_SuperBlock>(sector); bool audi = audiSb.magic == QNX6_MAGIC; if (audi) { sb.AppendLine("QNX6 (Audi) filesystem"); sb.AppendFormat("Checksum: 0x{0:X8}", audiSb.checksum).AppendLine(); sb.AppendFormat("Serial: 0x{0:X16}", audiSb.checksum).AppendLine(); sb.AppendFormat("{0} bytes per block", audiSb.blockSize).AppendLine(); sb.AppendFormat("{0} inodes free of {1}", audiSb.freeInodes, audiSb.numInodes).AppendLine(); sb.AppendFormat("{0} blocks ({1} bytes) free of {2} ({3} bytes)", audiSb.freeBlocks, audiSb.freeBlocks * audiSb.blockSize, audiSb.numBlocks, audiSb.numBlocks * audiSb.blockSize).AppendLine(); XmlFsType = new FileSystemType { Type = "QNX6 (Audi) filesystem", Clusters = audiSb.numBlocks, ClusterSize = audiSb.blockSize, Bootable = true, Files = audiSb.numInodes - audiSb.freeInodes, FilesSpecified = true, FreeClusters = audiSb.freeBlocks, FreeClustersSpecified = true, VolumeSerial = $"{audiSb.serial:X16}" }; //xmlFSType.VolumeName = CurrentEncoding.GetString(audiSb.id); information = sb.ToString(); return; } sb.AppendLine("QNX6 filesystem"); sb.AppendFormat("Checksum: 0x{0:X8}", qnxSb.checksum).AppendLine(); sb.AppendFormat("Serial: 0x{0:X16}", qnxSb.checksum).AppendLine(); sb.AppendFormat("Created on {0}", DateHandlers.UnixUnsignedToDateTime(qnxSb.ctime)).AppendLine(); sb.AppendFormat("Last mounted on {0}", DateHandlers.UnixUnsignedToDateTime(qnxSb.atime)).AppendLine(); sb.AppendFormat("Flags: 0x{0:X8}", qnxSb.flags).AppendLine(); sb.AppendFormat("Version1: 0x{0:X4}", qnxSb.version1).AppendLine(); sb.AppendFormat("Version2: 0x{0:X4}", qnxSb.version2).AppendLine(); //sb.AppendFormat("Volume ID: \"{0}\"", CurrentEncoding.GetString(qnxSb.volumeid)).AppendLine(); sb.AppendFormat("{0} bytes per block", qnxSb.blockSize).AppendLine(); sb.AppendFormat("{0} inodes free of {1}", qnxSb.freeInodes, qnxSb.numInodes).AppendLine(); sb.AppendFormat("{0} blocks ({1} bytes) free of {2} ({3} bytes)", qnxSb.freeBlocks, qnxSb.freeBlocks * qnxSb.blockSize, qnxSb.numBlocks, qnxSb.numBlocks * qnxSb.blockSize). AppendLine(); XmlFsType = new FileSystemType { Type = "QNX6 filesystem", Clusters = qnxSb.numBlocks, ClusterSize = qnxSb.blockSize, Bootable = true, Files = qnxSb.numInodes - qnxSb.freeInodes, FilesSpecified = true, FreeClusters = qnxSb.freeBlocks, FreeClustersSpecified = true, VolumeSerial = $"{qnxSb.serial:X16}", CreationDate = DateHandlers.UnixUnsignedToDateTime(qnxSb.ctime), CreationDateSpecified = true, ModificationDate = DateHandlers.UnixUnsignedToDateTime(qnxSb.atime), ModificationDateSpecified = true }; //xmlFSType.VolumeName = CurrentEncoding.GetString(qnxSb.volumeid); information = sb.ToString(); }
/// <inheritdoc /> public Errno Mount(IMediaImage imagePlugin, Partition partition, Encoding encoding, Dictionary <string, string> options, string @namespace) { XmlFsType = new FileSystemType(); options ??= GetDefaultOptions(); if (options.TryGetValue("debug", out string debugString)) { bool.TryParse(debugString, out _debug); } // Default namespace @namespace ??= "ecs"; switch (@namespace.ToLowerInvariant()) { case "dos": _namespace = Namespace.Dos; break; case "nt": _namespace = Namespace.Nt; break; case "os2": _namespace = Namespace.Os2; break; case "ecs": _namespace = Namespace.Ecs; break; case "lfn": _namespace = Namespace.Lfn; break; case "human": _namespace = Namespace.Human; break; default: return(Errno.InvalidArgument); } AaruConsole.DebugWriteLine("FAT plugin", "Reading BPB"); uint sectorsPerBpb = imagePlugin.Info.SectorSize < 512 ? 512 / imagePlugin.Info.SectorSize : 1; byte[] bpbSector = imagePlugin.ReadSectors(0 + partition.Start, sectorsPerBpb); BpbKind bpbKind = DetectBpbKind(bpbSector, imagePlugin, partition, out BiosParameterBlockEbpb fakeBpb, out HumanParameterBlock humanBpb, out AtariParameterBlock atariBpb, out byte minBootNearJump, out bool andosOemCorrect, out bool bootable); _fat12 = false; _fat16 = false; _fat32 = false; _useFirstFat = true; XmlFsType.Bootable = bootable; _statfs = new FileSystemInfo { Blocks = XmlFsType.Clusters, FilenameLength = 11, Files = 0, // Requires traversing all directories FreeFiles = 0, PluginId = Id, FreeBlocks = 0 // Requires traversing the FAT }; // This is needed because for FAT16, GEMDOS increases bytes per sector count instead of using big_sectors field. uint sectorsPerRealSector = 1; // This is needed because some OSes don't put volume label as first entry in the root directory uint sectorsForRootDirectory = 0; uint rootDirectoryCluster = 0; switch (bpbKind) { case BpbKind.DecRainbow: case BpbKind.Hardcoded: case BpbKind.Msx: case BpbKind.Apricot: _fat12 = true; break; case BpbKind.ShortFat32: case BpbKind.LongFat32: { _fat32 = true; Fat32ParameterBlock fat32Bpb = Marshal.ByteArrayToStructureLittleEndian <Fat32ParameterBlock>(bpbSector); Fat32ParameterBlockShort shortFat32Bpb = Marshal.ByteArrayToStructureLittleEndian <Fat32ParameterBlockShort>(bpbSector); rootDirectoryCluster = fat32Bpb.root_cluster; // This is to support FAT partitions on hybrid ISO/USB images if (imagePlugin.Info.XmlMediaType == XmlMediaType.OpticalDisc) { fat32Bpb.bps *= 4; fat32Bpb.spc /= 4; fat32Bpb.big_spfat /= 4; fat32Bpb.hsectors /= 4; fat32Bpb.sptrk /= 4; } XmlFsType.Type = fat32Bpb.version != 0 ? "FAT+" : "FAT32"; if (fat32Bpb.oem_name != null && (fat32Bpb.oem_name[5] != 0x49 || fat32Bpb.oem_name[6] != 0x48 || fat32Bpb.oem_name[7] != 0x43)) { XmlFsType.SystemIdentifier = StringHandlers.CToString(fat32Bpb.oem_name); } _sectorsPerCluster = fat32Bpb.spc; XmlFsType.ClusterSize = (uint)(fat32Bpb.bps * fat32Bpb.spc); _reservedSectors = fat32Bpb.rsectors; if (fat32Bpb.big_sectors == 0 && fat32Bpb.signature == 0x28) { XmlFsType.Clusters = shortFat32Bpb.huge_sectors / shortFat32Bpb.spc; } else { XmlFsType.Clusters = fat32Bpb.big_sectors / fat32Bpb.spc; } _sectorsPerFat = fat32Bpb.big_spfat; XmlFsType.VolumeSerial = $"{fat32Bpb.serial_no:X8}"; _statfs.Id = new FileSystemId { IsInt = true, Serial32 = fat32Bpb.serial_no }; if ((fat32Bpb.flags & 0xF8) == 0x00) { if ((fat32Bpb.flags & 0x01) == 0x01) { XmlFsType.Dirty = true; } } if ((fat32Bpb.mirror_flags & 0x80) == 0x80) { _useFirstFat = (fat32Bpb.mirror_flags & 0xF) != 1; } if (fat32Bpb.signature == 0x29) { XmlFsType.VolumeName = StringHandlers.SpacePaddedToString(fat32Bpb.volume_label, Encoding); XmlFsType.VolumeName = XmlFsType.VolumeName?.Replace("\0", ""); } // Check that jumps to a correct boot code position and has boot signature set. // This will mean that the volume will boot, even if just to say "this is not bootable change disk"...... XmlFsType.Bootable = (fat32Bpb.jump[0] == 0xEB && fat32Bpb.jump[1] >= minBootNearJump && fat32Bpb.jump[1] < 0x80) || (fat32Bpb.jump[0] == 0xE9 && fat32Bpb.jump.Length >= 3 && BitConverter.ToUInt16(fat32Bpb.jump, 1) >= minBootNearJump && BitConverter.ToUInt16(fat32Bpb.jump, 1) <= 0x1FC); sectorsPerRealSector = fat32Bpb.bps / imagePlugin.Info.SectorSize; _sectorsPerCluster *= sectorsPerRealSector; // First root directory sector _firstClusterSector = ((ulong)((fat32Bpb.big_spfat * fat32Bpb.fats_no) + fat32Bpb.rsectors) * sectorsPerRealSector) - (2 * _sectorsPerCluster); if (fat32Bpb.fsinfo_sector + partition.Start <= partition.End) { byte[] fsinfoSector = imagePlugin.ReadSector(fat32Bpb.fsinfo_sector + partition.Start); FsInfoSector fsInfo = Marshal.ByteArrayToStructureLittleEndian <FsInfoSector>(fsinfoSector); if (fsInfo.signature1 == FSINFO_SIGNATURE1 && fsInfo.signature2 == FSINFO_SIGNATURE2 && fsInfo.signature3 == FSINFO_SIGNATURE3) { if (fsInfo.free_clusters < 0xFFFFFFFF) { XmlFsType.FreeClusters = fsInfo.free_clusters; XmlFsType.FreeClustersSpecified = true; } } } break; } // Some fields could overflow fake BPB, those will be handled below case BpbKind.Atari: { ushort sum = 0; for (int i = 0; i < bpbSector.Length; i += 2) { sum += BigEndianBitConverter.ToUInt16(bpbSector, i); } // TODO: Check this if (sum == 0x1234) { XmlFsType.Bootable = true; } break; } case BpbKind.Human: // If not debug set Human68k namespace and ShiftJIS codepage as defaults if (!_debug) { _namespace = Namespace.Human; encoding = Encoding.GetEncoding("shift_jis"); } XmlFsType.Bootable = true; break; } Encoding = encoding ?? (bpbKind == BpbKind.Human ? Encoding.GetEncoding("shift_jis") : Encoding.GetEncoding("IBM437")); ulong firstRootSector = 0; if (!_fat32) { // This is to support FAT partitions on hybrid ISO/USB images if (imagePlugin.Info.XmlMediaType == XmlMediaType.OpticalDisc) { fakeBpb.bps *= 4; fakeBpb.spc /= 4; fakeBpb.spfat /= 4; fakeBpb.hsectors /= 4; fakeBpb.sptrk /= 4; fakeBpb.rsectors /= 4; if (fakeBpb.spc == 0) { fakeBpb.spc = 1; } } // This assumes no sane implementation will violate cluster size rules // However nothing prevents this to happen // If first file on disk uses only one cluster there is absolutely no way to differentiate between FAT12 and FAT16, // so let's hope implementations use common sense? if (!_fat12 && !_fat16) { ulong clusters; if (fakeBpb.sectors == 0) { clusters = fakeBpb.spc == 0 ? fakeBpb.big_sectors : fakeBpb.big_sectors / fakeBpb.spc; } else { clusters = fakeBpb.spc == 0 ? fakeBpb.sectors : (ulong)fakeBpb.sectors / fakeBpb.spc; } if (clusters < 4089) { _fat12 = true; } else { _fat16 = true; } } if (_fat12) { XmlFsType.Type = "FAT12"; } else if (_fat16) { XmlFsType.Type = "FAT16"; } if (bpbKind == BpbKind.Atari) { if (atariBpb.serial_no[0] != 0x49 || atariBpb.serial_no[1] != 0x48 || atariBpb.serial_no[2] != 0x43) { XmlFsType.VolumeSerial = $"{atariBpb.serial_no[0]:X2}{atariBpb.serial_no[1]:X2}{atariBpb.serial_no[2]:X2}"; _statfs.Id = new FileSystemId { IsInt = true, Serial32 = (uint)((atariBpb.serial_no[0] << 16) + (atariBpb.serial_no[1] << 8) + atariBpb.serial_no[2]) }; } XmlFsType.SystemIdentifier = StringHandlers.CToString(atariBpb.oem_name); if (string.IsNullOrEmpty(XmlFsType.SystemIdentifier)) { XmlFsType.SystemIdentifier = null; } } else if (fakeBpb.oem_name != null) { if (fakeBpb.oem_name[5] != 0x49 || fakeBpb.oem_name[6] != 0x48 || fakeBpb.oem_name[7] != 0x43) { // Later versions of Windows create a DOS 3 BPB without OEM name on 8 sectors/track floppies // OEM ID should be ASCII, otherwise ignore it if (fakeBpb.oem_name[0] >= 0x20 && fakeBpb.oem_name[0] <= 0x7F && fakeBpb.oem_name[1] >= 0x20 && fakeBpb.oem_name[1] <= 0x7F && fakeBpb.oem_name[2] >= 0x20 && fakeBpb.oem_name[2] <= 0x7F && fakeBpb.oem_name[3] >= 0x20 && fakeBpb.oem_name[3] <= 0x7F && fakeBpb.oem_name[4] >= 0x20 && fakeBpb.oem_name[4] <= 0x7F && fakeBpb.oem_name[5] >= 0x20 && fakeBpb.oem_name[5] <= 0x7F && fakeBpb.oem_name[6] >= 0x20 && fakeBpb.oem_name[6] <= 0x7F && fakeBpb.oem_name[7] >= 0x20 && fakeBpb.oem_name[7] <= 0x7F) { XmlFsType.SystemIdentifier = StringHandlers.CToString(fakeBpb.oem_name); } else if (fakeBpb.oem_name[0] < 0x20 && fakeBpb.oem_name[1] >= 0x20 && fakeBpb.oem_name[1] <= 0x7F && fakeBpb.oem_name[2] >= 0x20 && fakeBpb.oem_name[2] <= 0x7F && fakeBpb.oem_name[3] >= 0x20 && fakeBpb.oem_name[3] <= 0x7F && fakeBpb.oem_name[4] >= 0x20 && fakeBpb.oem_name[4] <= 0x7F && fakeBpb.oem_name[5] >= 0x20 && fakeBpb.oem_name[5] <= 0x7F && fakeBpb.oem_name[6] >= 0x20 && fakeBpb.oem_name[6] <= 0x7F && fakeBpb.oem_name[7] >= 0x20 && fakeBpb.oem_name[7] <= 0x7F) { XmlFsType.SystemIdentifier = StringHandlers.CToString(fakeBpb.oem_name, Encoding, start: 1); } } if (fakeBpb.signature == 0x28 || fakeBpb.signature == 0x29) { XmlFsType.VolumeSerial = $"{fakeBpb.serial_no:X8}"; _statfs.Id = new FileSystemId { IsInt = true, Serial32 = fakeBpb.serial_no }; } } if (bpbKind != BpbKind.Human) { if (fakeBpb.sectors == 0) { XmlFsType.Clusters = fakeBpb.spc == 0 ? fakeBpb.big_sectors : fakeBpb.big_sectors / fakeBpb.spc; } else { XmlFsType.Clusters = (ulong)(fakeBpb.spc == 0 ? fakeBpb.sectors : fakeBpb.sectors / fakeBpb.spc); } } else { XmlFsType.Clusters = humanBpb.clusters == 0 ? humanBpb.big_clusters : humanBpb.clusters; } _sectorsPerCluster = fakeBpb.spc; XmlFsType.ClusterSize = (uint)(fakeBpb.bps * fakeBpb.spc); _reservedSectors = fakeBpb.rsectors; _sectorsPerFat = fakeBpb.spfat; if (fakeBpb.signature == 0x28 || fakeBpb.signature == 0x29 || andosOemCorrect) { if ((fakeBpb.flags & 0xF8) == 0x00) { if ((fakeBpb.flags & 0x01) == 0x01) { XmlFsType.Dirty = true; } } if (fakeBpb.signature == 0x29 || andosOemCorrect) { XmlFsType.VolumeName = StringHandlers.SpacePaddedToString(fakeBpb.volume_label, Encoding); XmlFsType.VolumeName = XmlFsType.VolumeName?.Replace("\0", ""); } } // Workaround that PCExchange jumps into "FAT16 "... if (XmlFsType.SystemIdentifier == "PCX 2.0 ") { fakeBpb.jump[1] += 8; } // Check that jumps to a correct boot code position and has boot signature set. // This will mean that the volume will boot, even if just to say "this is not bootable change disk"...... if (XmlFsType.Bootable == false && fakeBpb.jump != null) { XmlFsType.Bootable |= (fakeBpb.jump[0] == 0xEB && fakeBpb.jump[1] >= minBootNearJump && fakeBpb.jump[1] < 0x80) || (fakeBpb.jump[0] == 0xE9 && fakeBpb.jump.Length >= 3 && BitConverter.ToUInt16(fakeBpb.jump, 1) >= minBootNearJump && BitConverter.ToUInt16(fakeBpb.jump, 1) <= 0x1FC); } // First root directory sector firstRootSector = ((ulong)((fakeBpb.spfat * fakeBpb.fats_no) + fakeBpb.rsectors) * sectorsPerRealSector) + partition.Start; sectorsForRootDirectory = (uint)((fakeBpb.root_ent * 32) / imagePlugin.Info.SectorSize); sectorsPerRealSector = fakeBpb.bps / imagePlugin.Info.SectorSize; _sectorsPerCluster *= sectorsPerRealSector; } _firstClusterSector += partition.Start; _image = imagePlugin; if (_fat32) { _fatEntriesPerSector = imagePlugin.Info.SectorSize / 4; } else if (_fat16) { _fatEntriesPerSector = imagePlugin.Info.SectorSize / 2; } else { _fatEntriesPerSector = (imagePlugin.Info.SectorSize * 2) / 3; } _fatFirstSector = partition.Start + (_reservedSectors * sectorsPerRealSector); _rootDirectoryCache = new Dictionary <string, CompleteDirectoryEntry>(); byte[] rootDirectory; if (!_fat32) { _firstClusterSector = (firstRootSector + sectorsForRootDirectory) - (_sectorsPerCluster * 2); rootDirectory = imagePlugin.ReadSectors(firstRootSector, sectorsForRootDirectory); if (bpbKind == BpbKind.DecRainbow) { var rootMs = new MemoryStream(); foreach (byte[] tmp in from ulong rootSector in new[] { 0x17, 0x19, 0x1B, 0x1D, 0x1E, 0x20 } select imagePlugin.ReadSector(rootSector)) { rootMs.Write(tmp, 0, tmp.Length); } rootDirectory = rootMs.ToArray(); } } else { if (rootDirectoryCluster == 0) { return(Errno.InvalidArgument); } var rootMs = new MemoryStream(); uint[] rootDirectoryClusters = GetClusters(rootDirectoryCluster); foreach (byte[] buffer in rootDirectoryClusters.Select(cluster => imagePlugin. ReadSectors(_firstClusterSector + (cluster * _sectorsPerCluster), _sectorsPerCluster))) { rootMs.Write(buffer, 0, buffer.Length); } rootDirectory = rootMs.ToArray(); // OS/2 FAT32.IFS uses LFN instead of .LONGNAME if (_namespace == Namespace.Os2) { _namespace = Namespace.Os2; } } if (rootDirectory is null) { return(Errno.InvalidArgument); } byte[] lastLfnName = null; byte lastLfnChecksum = 0; for (int i = 0; i < rootDirectory.Length; i += Marshal.SizeOf <DirectoryEntry>()) { DirectoryEntry entry = Marshal.ByteArrayToStructureLittleEndian <DirectoryEntry>(rootDirectory, i, Marshal.SizeOf <DirectoryEntry>()); if (entry.filename[0] == DIRENT_FINISHED) { break; } if (entry.attributes.HasFlag(FatAttributes.LFN)) { if (_namespace != Namespace.Lfn && _namespace != Namespace.Ecs) { continue; } LfnEntry lfnEntry = Marshal.ByteArrayToStructureLittleEndian <LfnEntry>(rootDirectory, i, Marshal.SizeOf <LfnEntry>()); int lfnSequence = lfnEntry.sequence & LFN_MASK; if ((lfnEntry.sequence & LFN_ERASED) > 0) { continue; } if ((lfnEntry.sequence & LFN_LAST) > 0) { lastLfnName = new byte[lfnSequence * 26]; lastLfnChecksum = lfnEntry.checksum; } if (lastLfnName is null) { continue; } if (lfnEntry.checksum != lastLfnChecksum) { continue; } lfnSequence--; Array.Copy(lfnEntry.name1, 0, lastLfnName, lfnSequence * 26, 10); Array.Copy(lfnEntry.name2, 0, lastLfnName, (lfnSequence * 26) + 10, 12); Array.Copy(lfnEntry.name3, 0, lastLfnName, (lfnSequence * 26) + 22, 4); continue; } // Not a correct entry if (entry.filename[0] < DIRENT_MIN && entry.filename[0] != DIRENT_E5) { continue; } // Self if (Encoding.GetString(entry.filename).TrimEnd() == ".") { continue; } // Parent if (Encoding.GetString(entry.filename).TrimEnd() == "..") { continue; } // Deleted if (entry.filename[0] == DIRENT_DELETED) { continue; } string filename; if (entry.attributes.HasFlag(FatAttributes.VolumeLabel)) { byte[] fullname = new byte[11]; Array.Copy(entry.filename, 0, fullname, 0, 8); Array.Copy(entry.extension, 0, fullname, 8, 3); string volname = Encoding.GetString(fullname).Trim(); if (!string.IsNullOrEmpty(volname)) { XmlFsType.VolumeName = entry.caseinfo.HasFlag(CaseInfo.AllLowerCase) && _namespace == Namespace.Nt ? volname.ToLower() : volname; } XmlFsType.VolumeName = XmlFsType.VolumeName?.Replace("\0", ""); if (entry.ctime > 0 && entry.cdate > 0) { XmlFsType.CreationDate = DateHandlers.DosToDateTime(entry.cdate, entry.ctime); if (entry.ctime_ms > 0) { XmlFsType.CreationDate = XmlFsType.CreationDate.AddMilliseconds(entry.ctime_ms * 10); } XmlFsType.CreationDateSpecified = true; } if (entry.mtime > 0 && entry.mdate > 0) { XmlFsType.ModificationDate = DateHandlers.DosToDateTime(entry.mdate, entry.mtime); XmlFsType.ModificationDateSpecified = true; } continue; } var completeEntry = new CompleteDirectoryEntry { Dirent = entry }; if ((_namespace == Namespace.Lfn || _namespace == Namespace.Ecs) && lastLfnName != null) { byte calculatedLfnChecksum = LfnChecksum(entry.filename, entry.extension); if (calculatedLfnChecksum == lastLfnChecksum) { filename = StringHandlers.CToString(lastLfnName, Encoding.Unicode, true); completeEntry.Lfn = filename; lastLfnName = null; lastLfnChecksum = 0; } } if (entry.filename[0] == DIRENT_E5) { entry.filename[0] = DIRENT_DELETED; } string name = Encoding.GetString(entry.filename).TrimEnd(); string extension = Encoding.GetString(entry.extension).TrimEnd(); if (_namespace == Namespace.Nt) { if (entry.caseinfo.HasFlag(CaseInfo.LowerCaseExtension)) { extension = extension.ToLower(CultureInfo.CurrentCulture); } if (entry.caseinfo.HasFlag(CaseInfo.LowerCaseBasename)) { name = name.ToLower(CultureInfo.CurrentCulture); } } if (extension != "") { filename = name + "." + extension; } else { filename = name; } completeEntry.Shortname = filename; if (_namespace == Namespace.Human) { HumanDirectoryEntry humanEntry = Marshal.ByteArrayToStructureLittleEndian <HumanDirectoryEntry>(rootDirectory, i, Marshal.SizeOf <HumanDirectoryEntry>()); completeEntry.HumanDirent = humanEntry; name = StringHandlers.CToString(humanEntry.name1, Encoding).TrimEnd(); extension = StringHandlers.CToString(humanEntry.extension, Encoding).TrimEnd(); string name2 = StringHandlers.CToString(humanEntry.name2, Encoding).TrimEnd(); if (extension != "") { filename = name + name2 + "." + extension; } else { filename = name + name2; } completeEntry.HumanName = filename; } if (!_fat32 && filename == "EA DATA. SF") { _eaDirEntry = entry; lastLfnName = null; lastLfnChecksum = 0; if (_debug) { _rootDirectoryCache[completeEntry.ToString()] = completeEntry; } continue; } _rootDirectoryCache[completeEntry.ToString()] = completeEntry; lastLfnName = null; lastLfnChecksum = 0; } XmlFsType.VolumeName = XmlFsType.VolumeName?.Trim(); _statfs.Blocks = XmlFsType.Clusters; switch (bpbKind) { case BpbKind.Hardcoded: _statfs.Type = $"Microsoft FAT{(_fat16 ? "16" : "12")}"; break; case BpbKind.Atari: _statfs.Type = $"Atari FAT{(_fat16 ? "16" : "12")}"; break; case BpbKind.Msx: _statfs.Type = $"MSX FAT{(_fat16 ? "16" : "12")}"; break; case BpbKind.Dos2: case BpbKind.Dos3: case BpbKind.Dos32: case BpbKind.Dos33: case BpbKind.ShortExtended: case BpbKind.Extended: _statfs.Type = $"Microsoft FAT{(_fat16 ? "16" : "12")}"; break; case BpbKind.ShortFat32: case BpbKind.LongFat32: _statfs.Type = XmlFsType.Type == "FAT+" ? "FAT+" : "Microsoft FAT32"; break; case BpbKind.Andos: _statfs.Type = $"ANDOS FAT{(_fat16 ? "16" : "12")}"; break; case BpbKind.Apricot: _statfs.Type = $"Apricot FAT{(_fat16 ? "16" : "12")}"; break; case BpbKind.DecRainbow: _statfs.Type = $"DEC FAT{(_fat16 ? "16" : "12")}"; break; case BpbKind.Human: _statfs.Type = $"Human68k FAT{(_fat16 ? "16" : "12")}"; break; default: throw new ArgumentOutOfRangeException(); } _bytesPerCluster = _sectorsPerCluster * imagePlugin.Info.SectorSize; if (_fat12) { byte[] fatBytes = imagePlugin.ReadSectors(_fatFirstSector + (_useFirstFat ? 0 : _sectorsPerFat), _sectorsPerFat); _fatEntries = new ushort[_statfs.Blocks]; int pos = 0; for (int i = 0; i + 3 < fatBytes.Length && pos < _fatEntries.Length; i += 3) { _fatEntries[pos++] = (ushort)(((fatBytes[i + 1] & 0xF) << 8) + fatBytes[i + 0]); _fatEntries[pos++] = (ushort)(((fatBytes[i + 1] & 0xF0) >> 4) + (fatBytes[i + 2] << 4)); } } else if (_fat16) { AaruConsole.DebugWriteLine("FAT plugin", "Reading FAT16"); byte[] fatBytes = imagePlugin.ReadSectors(_fatFirstSector + (_useFirstFat ? 0 : _sectorsPerFat), _sectorsPerFat); AaruConsole.DebugWriteLine("FAT plugin", "Casting FAT"); _fatEntries = MemoryMarshal.Cast <byte, ushort>(fatBytes).ToArray(); } // TODO: Check how this affects international filenames _cultureInfo = new CultureInfo("en-US", false); _directoryCache = new Dictionary <string, Dictionary <string, CompleteDirectoryEntry> >(); // Check it is really an OS/2 EA file if (_eaDirEntry.start_cluster != 0) { CacheEaData(); ushort eamagic = BitConverter.ToUInt16(_cachedEaData, 0); if (eamagic != EADATA_MAGIC) { _eaDirEntry = new DirectoryEntry(); _cachedEaData = null; } else { _eaCache = new Dictionary <string, Dictionary <string, byte[]> >(); } } else if (_fat32) { _eaCache = new Dictionary <string, Dictionary <string, byte[]> >(); } // Check OS/2 .LONGNAME if (_eaCache != null && (_namespace == Namespace.Os2 || _namespace == Namespace.Ecs) && !_fat32) { List <KeyValuePair <string, CompleteDirectoryEntry> > rootFilesWithEas = _rootDirectoryCache.Where(t => t.Value.Dirent.ea_handle != 0).ToList(); foreach (KeyValuePair <string, CompleteDirectoryEntry> fileWithEa in rootFilesWithEas) { Dictionary <string, byte[]> eas = GetEas(fileWithEa.Value.Dirent.ea_handle); if (eas is null) { continue; } if (!eas.TryGetValue("com.microsoft.os2.longname", out byte[] longnameEa))
public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, Encoding encoding) { Encoding = encoding ?? Encoding.GetEncoding("iso-8859-15"); information = ""; StringBuilder sb = new StringBuilder(); ulong sector = AFS_BOOTBLOCK_SIZE / imagePlugin.Info.SectorSize; uint offset = AFS_BOOTBLOCK_SIZE % imagePlugin.Info.SectorSize; uint run = 1; if (imagePlugin.Info.SectorSize < AFS_SUPERBLOCK_SIZE) { run = AFS_SUPERBLOCK_SIZE / imagePlugin.Info.SectorSize; } byte[] tmp = imagePlugin.ReadSectors(sector + partition.Start, run); byte[] sbSector = new byte[AFS_SUPERBLOCK_SIZE]; Array.Copy(tmp, offset, sbSector, 0, AFS_SUPERBLOCK_SIZE); AtheosSuperBlock afsSb = Marshal.ByteArrayToStructureLittleEndian <AtheosSuperBlock>(sbSector); sb.AppendLine("Atheos filesystem"); if (afsSb.flags == 1) { sb.AppendLine("Filesystem is read-only"); } sb.AppendFormat("Volume name: {0}", StringHandlers.CToString(afsSb.name, Encoding)).AppendLine(); sb.AppendFormat("{0} bytes per block", afsSb.block_size).AppendLine(); sb.AppendFormat("{0} blocks in volume ({1} bytes)", afsSb.num_blocks, afsSb.num_blocks * afsSb.block_size) .AppendLine(); sb.AppendFormat("{0} used blocks ({1} bytes)", afsSb.used_blocks, afsSb.used_blocks * afsSb.block_size) .AppendLine(); sb.AppendFormat("{0} bytes per i-node", afsSb.inode_size).AppendLine(); sb.AppendFormat("{0} blocks per allocation group ({1} bytes)", afsSb.blocks_per_ag, afsSb.blocks_per_ag * afsSb.block_size).AppendLine(); sb.AppendFormat("{0} allocation groups in volume", afsSb.num_ags).AppendLine(); sb.AppendFormat("Journal resides in block {0} of allocation group {1} and runs for {2} blocks ({3} bytes)", afsSb.log_blocks_start, afsSb.log_blocks_ag, afsSb.log_blocks_len, afsSb.log_blocks_len * afsSb.block_size).AppendLine(); sb.AppendFormat("Journal starts in byte {0} and has {1} bytes in {2} blocks", afsSb.log_start, afsSb.log_size, afsSb.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)", afsSb.root_dir_start, afsSb.root_dir_ag, afsSb.root_dir_len, afsSb.root_dir_len * afsSb.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)", afsSb.deleted_start, afsSb.deleted_ag, afsSb.deleted_len, afsSb.deleted_len * afsSb.block_size).AppendLine(); sb .AppendFormat("Indices' i-node resides in block {0} of allocation group {1} and runs for {2} blocks ({3} bytes)", afsSb.indices_start, afsSb.indices_ag, afsSb.indices_len, afsSb.indices_len * afsSb.block_size).AppendLine(); sb.AppendFormat("{0} blocks for bootloader ({1} bytes)", afsSb.boot_size, afsSb.boot_size * afsSb.block_size).AppendLine(); information = sb.ToString(); XmlFsType = new FileSystemType { Clusters = (ulong)afsSb.num_blocks, ClusterSize = afsSb.block_size, Dirty = false, FreeClusters = (ulong)(afsSb.num_blocks - afsSb.used_blocks), FreeClustersSpecified = true, Type = "AtheOS filesystem", VolumeName = StringHandlers.CToString(afsSb.name, Encoding) }; }
public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, Encoding encoding) { Encoding = encoding ?? Encoding.GetEncoding("iso-8859-15"); information = ""; var sb = new StringBuilder(); bool newExt2 = false; bool ext3 = false; bool ext4 = false; int sbSizeInBytes = Marshal.SizeOf <SuperBlock>(); uint sbSizeInSectors = (uint)(sbSizeInBytes / imagePlugin.Info.SectorSize); if (sbSizeInBytes % imagePlugin.Info.SectorSize > 0) { sbSizeInSectors++; } ulong sbSectorOff = SB_POS / imagePlugin.Info.SectorSize; uint sbOff = SB_POS % imagePlugin.Info.SectorSize; byte[] sbSector = imagePlugin.ReadSectors(sbSectorOff + partition.Start, sbSizeInSectors); byte[] sblock = new byte[sbSizeInBytes]; Array.Copy(sbSector, sbOff, sblock, 0, sbSizeInBytes); SuperBlock supblk = Marshal.ByteArrayToStructureLittleEndian <SuperBlock>(sblock); XmlFsType = new FileSystemType(); switch (supblk.magic) { case EXT2_MAGIC_OLD: sb.AppendLine("ext2 (old) filesystem"); XmlFsType.Type = "ext2"; break; case EXT2_MAGIC: ext3 |= (supblk.ftr_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL) == EXT3_FEATURE_COMPAT_HAS_JOURNAL || (supblk.ftr_incompat & EXT3_FEATURE_INCOMPAT_RECOVER) == EXT3_FEATURE_INCOMPAT_RECOVER || (supblk.ftr_incompat & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) == EXT3_FEATURE_INCOMPAT_JOURNAL_DEV; if ((supblk.ftr_ro_compat & EXT4_FEATURE_RO_COMPAT_HUGE_FILE) == EXT4_FEATURE_RO_COMPAT_HUGE_FILE || (supblk.ftr_ro_compat & EXT4_FEATURE_RO_COMPAT_GDT_CSUM) == EXT4_FEATURE_RO_COMPAT_GDT_CSUM || (supblk.ftr_ro_compat & EXT4_FEATURE_RO_COMPAT_DIR_NLINK) == EXT4_FEATURE_RO_COMPAT_DIR_NLINK || (supblk.ftr_ro_compat & EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE) == EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE || (supblk.ftr_incompat & EXT4_FEATURE_INCOMPAT_64BIT) == EXT4_FEATURE_INCOMPAT_64BIT || (supblk.ftr_incompat & EXT4_FEATURE_INCOMPAT_MMP) == EXT4_FEATURE_INCOMPAT_MMP || (supblk.ftr_incompat & EXT4_FEATURE_INCOMPAT_FLEX_BG) == EXT4_FEATURE_INCOMPAT_FLEX_BG || (supblk.ftr_incompat & EXT4_FEATURE_INCOMPAT_EA_INODE) == EXT4_FEATURE_INCOMPAT_EA_INODE || (supblk.ftr_incompat & EXT4_FEATURE_INCOMPAT_DIRDATA) == EXT4_FEATURE_INCOMPAT_DIRDATA) { ext3 = false; ext4 = true; } newExt2 |= !ext3 && !ext4; if (newExt2) { sb.AppendLine("ext2 filesystem"); XmlFsType.Type = "ext2"; } if (ext3) { sb.AppendLine("ext3 filesystem"); XmlFsType.Type = "ext3"; } if (ext4) { sb.AppendLine("ext4 filesystem"); XmlFsType.Type = "ext4"; } break; default: information = "Not a ext2/3/4 filesystem" + Environment.NewLine; return; } string extOs; switch (supblk.creator_os) { case EXT2_OS_FREEBSD: extOs = "FreeBSD"; break; case EXT2_OS_HURD: extOs = "Hurd"; break; case EXT2_OS_LINUX: extOs = "Linux"; break; case EXT2_OS_LITES: extOs = "Lites"; break; case EXT2_OS_MASIX: extOs = "MasIX"; break; default: extOs = $"Unknown OS ({supblk.creator_os})"; break; } XmlFsType.SystemIdentifier = extOs; if (supblk.mkfs_t > 0) { sb.AppendFormat("Volume was created on {0} for {1}", DateHandlers.UnixUnsignedToDateTime(supblk.mkfs_t), extOs).AppendLine(); XmlFsType.CreationDate = DateHandlers.UnixUnsignedToDateTime(supblk.mkfs_t); XmlFsType.CreationDateSpecified = true; } else { sb.AppendFormat("Volume was created for {0}", extOs).AppendLine(); } byte[] tempBytes = new byte[8]; ulong blocks, reserved, free; if ((supblk.ftr_incompat & EXT4_FEATURE_INCOMPAT_64BIT) == EXT4_FEATURE_INCOMPAT_64BIT) { byte[] tempLo = BitConverter.GetBytes(supblk.blocks); byte[] tempHi = BitConverter.GetBytes(supblk.blocks_hi); tempBytes[0] = tempLo[0]; tempBytes[1] = tempLo[1]; tempBytes[2] = tempLo[2]; tempBytes[3] = tempLo[3]; tempBytes[4] = tempHi[0]; tempBytes[5] = tempHi[1]; tempBytes[6] = tempHi[2]; tempBytes[7] = tempHi[3]; blocks = BitConverter.ToUInt64(tempBytes, 0); tempLo = BitConverter.GetBytes(supblk.reserved_blocks); tempHi = BitConverter.GetBytes(supblk.reserved_blocks_hi); tempBytes[0] = tempLo[0]; tempBytes[1] = tempLo[1]; tempBytes[2] = tempLo[2]; tempBytes[3] = tempLo[3]; tempBytes[4] = tempHi[0]; tempBytes[5] = tempHi[1]; tempBytes[6] = tempHi[2]; tempBytes[7] = tempHi[3]; reserved = BitConverter.ToUInt64(tempBytes, 0); tempLo = BitConverter.GetBytes(supblk.free_blocks); tempHi = BitConverter.GetBytes(supblk.free_blocks_hi); tempBytes[0] = tempLo[0]; tempBytes[1] = tempLo[1]; tempBytes[2] = tempLo[2]; tempBytes[3] = tempLo[3]; tempBytes[4] = tempHi[0]; tempBytes[5] = tempHi[1]; tempBytes[6] = tempHi[2]; tempBytes[7] = tempHi[3]; free = BitConverter.ToUInt64(tempBytes, 0); } else { blocks = supblk.blocks; reserved = supblk.reserved_blocks; free = supblk.free_blocks; } if (supblk.block_size == 0) // Then it is 1024 bytes { supblk.block_size = 1024; } sb.AppendFormat("Volume has {0} blocks of {1} bytes, for a total of {2} bytes", blocks, 1024 << (int)supblk.block_size, blocks * (ulong)(1024 << (int)supblk.block_size)). AppendLine(); XmlFsType.Clusters = blocks; XmlFsType.ClusterSize = (uint)(1024 << (int)supblk.block_size); if (supblk.mount_t > 0 || supblk.mount_c > 0) { if (supblk.mount_t > 0) { sb.AppendFormat("Last mounted on {0}", DateHandlers.UnixUnsignedToDateTime(supblk.mount_t)). AppendLine(); } if (supblk.max_mount_c != -1) { sb.AppendFormat("Volume has been mounted {0} times of a maximum of {1} mounts before checking", supblk.mount_c, supblk.max_mount_c).AppendLine(); } else { sb.AppendFormat("Volume has been mounted {0} times with no maximum no. of mounts before checking", supblk.mount_c).AppendLine(); } if (!string.IsNullOrEmpty(StringHandlers.CToString(supblk.last_mount_dir, Encoding))) { sb.AppendFormat("Last mounted on: \"{0}\"", StringHandlers.CToString(supblk.last_mount_dir, Encoding)).AppendLine(); } if (!string.IsNullOrEmpty(StringHandlers.CToString(supblk.mount_options, Encoding))) { sb.AppendFormat("Last used mount options were: {0}", StringHandlers.CToString(supblk.mount_options, Encoding)).AppendLine(); } } else { sb.AppendLine("Volume has never been mounted"); if (supblk.max_mount_c != -1) { sb.AppendFormat("Volume can be mounted {0} times before checking", supblk.max_mount_c).AppendLine(); } else { sb.AppendLine("Volume has no maximum no. of mounts before checking"); } } if (supblk.check_t > 0) { if (supblk.check_inv > 0) { sb.AppendFormat("Last checked on {0} (should check every {1} seconds)", DateHandlers.UnixUnsignedToDateTime(supblk.check_t), supblk.check_inv).AppendLine(); } else { sb.AppendFormat("Last checked on {0}", DateHandlers.UnixUnsignedToDateTime(supblk.check_t)). AppendLine(); } } else { if (supblk.check_inv > 0) { sb.AppendFormat("Volume has never been checked (should check every {0})", supblk.check_inv). AppendLine(); } else { sb.AppendLine("Volume has never been checked"); } } if (supblk.write_t > 0) { sb.AppendFormat("Last written on {0}", DateHandlers.UnixUnsignedToDateTime(supblk.write_t)). AppendLine(); XmlFsType.ModificationDate = DateHandlers.UnixUnsignedToDateTime(supblk.write_t); XmlFsType.ModificationDateSpecified = true; } else { sb.AppendLine("Volume has never been written"); } XmlFsType.Dirty = true; switch (supblk.state) { case EXT2_VALID_FS: sb.AppendLine("Volume is clean"); XmlFsType.Dirty = false; break; case EXT2_ERROR_FS: sb.AppendLine("Volume is dirty"); break; case EXT3_ORPHAN_FS: sb.AppendLine("Volume is recovering orphan files"); break; default: sb.AppendFormat("Volume is in an unknown state ({0})", supblk.state).AppendLine(); break; } if (!string.IsNullOrEmpty(StringHandlers.CToString(supblk.volume_name, Encoding))) { sb.AppendFormat("Volume name: \"{0}\"", StringHandlers.CToString(supblk.volume_name, Encoding)). AppendLine(); XmlFsType.VolumeName = StringHandlers.CToString(supblk.volume_name, Encoding); } switch (supblk.err_behaviour) { case EXT2_ERRORS_CONTINUE: sb.AppendLine("On errors, filesystem should continue"); break; case EXT2_ERRORS_RO: sb.AppendLine("On errors, filesystem should remount read-only"); break; case EXT2_ERRORS_PANIC: sb.AppendLine("On errors, filesystem should panic"); break; default: sb.AppendFormat("On errors filesystem will do an unknown thing ({0})", supblk.err_behaviour). AppendLine(); break; } if (supblk.revision > 0) { sb.AppendFormat("Filesystem revision: {0}.{1}", supblk.revision, supblk.minor_revision).AppendLine(); } if (supblk.uuid != Guid.Empty) { sb.AppendFormat("Volume UUID: {0}", supblk.uuid).AppendLine(); XmlFsType.VolumeSerial = supblk.uuid.ToString(); } if (supblk.kbytes_written > 0) { sb.AppendFormat("{0} KiB has been written on volume", supblk.kbytes_written).AppendLine(); } sb.AppendFormat("{0} reserved and {1} free blocks", reserved, free).AppendLine(); XmlFsType.FreeClusters = free; XmlFsType.FreeClustersSpecified = true; sb.AppendFormat("{0} inodes with {1} free inodes ({2}%)", supblk.inodes, supblk.free_inodes, (supblk.free_inodes * 100) / supblk.inodes).AppendLine(); if (supblk.first_inode > 0) { sb.AppendFormat("First inode is {0}", supblk.first_inode).AppendLine(); } if (supblk.frag_size > 0) { sb.AppendFormat("{0} bytes per fragment", supblk.frag_size).AppendLine(); } if (supblk.blocks_per_grp > 0 && supblk.flags_per_grp > 0 && supblk.inodes_per_grp > 0) { sb.AppendFormat("{0} blocks, {1} flags and {2} inodes per group", supblk.blocks_per_grp, supblk.flags_per_grp, supblk.inodes_per_grp).AppendLine(); } if (supblk.first_block > 0) { sb.AppendFormat("{0} is first data block", supblk.first_block).AppendLine(); } sb.AppendFormat("Default UID: {0}, GID: {1}", supblk.default_uid, supblk.default_gid).AppendLine(); if (supblk.block_group_no > 0) { sb.AppendFormat("Block group number is {0}", supblk.block_group_no).AppendLine(); } if (supblk.desc_grp_size > 0) { sb.AppendFormat("Group descriptor size is {0} bytes", supblk.desc_grp_size).AppendLine(); } if (supblk.first_meta_bg > 0) { sb.AppendFormat("First metablock group is {0}", supblk.first_meta_bg).AppendLine(); } if (supblk.raid_stride > 0) { sb.AppendFormat("RAID stride: {0}", supblk.raid_stride).AppendLine(); } if (supblk.raid_stripe_width > 0) { sb.AppendFormat("{0} blocks on all data disks", supblk.raid_stripe_width).AppendLine(); } if (supblk.mmp_interval > 0 && supblk.mmp_block > 0) { sb.AppendFormat("{0} seconds for multi-mount protection wait, on block {1}", supblk.mmp_interval, supblk.mmp_block).AppendLine(); } if (supblk.flex_bg_grp_size > 0) { sb.AppendFormat("{0} Flexible block group size", supblk.flex_bg_grp_size).AppendLine(); } if (supblk.hash_seed_1 > 0 && supblk.hash_seed_2 > 0 && supblk.hash_seed_3 > 0 && supblk.hash_seed_4 > 0) { sb.AppendFormat("Hash seed: {0:X8}{1:X8}{2:X8}{3:X8}, version {4}", supblk.hash_seed_1, supblk.hash_seed_2, supblk.hash_seed_3, supblk.hash_seed_4, supblk.hash_version). AppendLine(); } if ((supblk.ftr_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL) == EXT3_FEATURE_COMPAT_HAS_JOURNAL || (supblk.ftr_incompat & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) == EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) { sb.AppendLine("Volume is journaled"); if (supblk.journal_uuid != Guid.Empty) { sb.AppendFormat("Journal UUID: {0}", supblk.journal_uuid).AppendLine(); } sb.AppendFormat("Journal has inode {0}", supblk.journal_inode).AppendLine(); if ((supblk.ftr_compat & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) == EXT3_FEATURE_INCOMPAT_JOURNAL_DEV && supblk.journal_dev > 0) { sb.AppendFormat("Journal is on device {0}", supblk.journal_dev).AppendLine(); } if (supblk.jnl_backup_type > 0) { sb.AppendFormat("Journal backup type: {0}", supblk.jnl_backup_type).AppendLine(); } if (supblk.last_orphan > 0) { sb.AppendFormat("Last orphaned inode is {0}", supblk.last_orphan).AppendLine(); } else { sb.AppendLine("There are no orphaned inodes"); } } if (ext4) { if (supblk.snapshot_id > 0) { sb. AppendFormat("Active snapshot has ID {0}, on inode {1}, with {2} blocks reserved, list starting on block {3}", supblk.snapshot_id, supblk.snapshot_inum, supblk.snapshot_blocks, supblk.snapshot_list).AppendLine(); } if (supblk.error_count > 0) { sb.AppendFormat("{0} errors registered", supblk.error_count).AppendLine(); sb.AppendFormat("First error occurred on {0}, last on {1}", DateHandlers.UnixUnsignedToDateTime(supblk.first_error_t), DateHandlers.UnixUnsignedToDateTime(supblk.last_error_t)).AppendLine(); sb.AppendFormat("First error inode is {0}, last is {1}", supblk.first_error_inode, supblk.last_error_inode).AppendLine(); sb.AppendFormat("First error block is {0}, last is {1}", supblk.first_error_block, supblk.last_error_block).AppendLine(); sb.AppendFormat("First error function is \"{0}\", last is \"{1}\"", supblk.first_error_func, supblk.last_error_func).AppendLine(); } } sb.AppendFormat("Flags…:").AppendLine(); if ((supblk.flags & EXT2_FLAGS_SIGNED_HASH) == EXT2_FLAGS_SIGNED_HASH) { sb.AppendLine("Signed directory hash is in use"); } if ((supblk.flags & EXT2_FLAGS_UNSIGNED_HASH) == EXT2_FLAGS_UNSIGNED_HASH) { sb.AppendLine("Unsigned directory hash is in use"); } if ((supblk.flags & EXT2_FLAGS_TEST_FILESYS) == EXT2_FLAGS_TEST_FILESYS) { sb.AppendLine("Volume is testing development code"); } if ((supblk.flags & 0xFFFFFFF8) != 0) { sb.AppendFormat("Unknown set flags: {0:X8}", supblk.flags); } sb.AppendLine(); sb.AppendFormat("Default mount options…:").AppendLine(); if ((supblk.default_mnt_opts & EXT2_DEFM_DEBUG) == EXT2_DEFM_DEBUG) { sb.AppendLine("(debug): Enable debugging code"); } if ((supblk.default_mnt_opts & EXT2_DEFM_BSDGROUPS) == EXT2_DEFM_BSDGROUPS) { sb.AppendLine("(bsdgroups): Emulate BSD behaviour when creating new files"); } if ((supblk.default_mnt_opts & EXT2_DEFM_XATTR_USER) == EXT2_DEFM_XATTR_USER) { sb.AppendLine("(user_xattr): Enable user-specified extended attributes"); } if ((supblk.default_mnt_opts & EXT2_DEFM_ACL) == EXT2_DEFM_ACL) { sb.AppendLine("(acl): Enable POSIX ACLs"); } if ((supblk.default_mnt_opts & EXT2_DEFM_UID16) == EXT2_DEFM_UID16) { sb.AppendLine("(uid16): Disable 32bit UIDs and GIDs"); } if ((supblk.default_mnt_opts & EXT3_DEFM_JMODE_DATA) == EXT3_DEFM_JMODE_DATA) { sb.AppendLine("(journal_data): Journal data and metadata"); } if ((supblk.default_mnt_opts & EXT3_DEFM_JMODE_ORDERED) == EXT3_DEFM_JMODE_ORDERED) { sb.AppendLine("(journal_data_ordered): Write data before journaling metadata"); } if ((supblk.default_mnt_opts & EXT3_DEFM_JMODE_WBACK) == EXT3_DEFM_JMODE_WBACK) { sb.AppendLine("(journal_data_writeback): Write journal before data"); } if ((supblk.default_mnt_opts & 0xFFFFFE20) != 0) { sb.AppendFormat("Unknown set default mount options: {0:X8}", supblk.default_mnt_opts); } sb.AppendLine(); sb.AppendFormat("Compatible features…:").AppendLine(); if ((supblk.ftr_compat & EXT2_FEATURE_COMPAT_DIR_PREALLOC) == EXT2_FEATURE_COMPAT_DIR_PREALLOC) { sb.AppendLine("Pre-allocate directories"); } if ((supblk.ftr_compat & EXT2_FEATURE_COMPAT_IMAGIC_INODES) == EXT2_FEATURE_COMPAT_IMAGIC_INODES) { sb.AppendLine("imagic inodes ?"); } if ((supblk.ftr_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL) == EXT3_FEATURE_COMPAT_HAS_JOURNAL) { sb.AppendLine("Has journal (ext3)"); } if ((supblk.ftr_compat & EXT2_FEATURE_COMPAT_EXT_ATTR) == EXT2_FEATURE_COMPAT_EXT_ATTR) { sb.AppendLine("Has extended attribute blocks"); } if ((supblk.ftr_compat & EXT2_FEATURE_COMPAT_RESIZE_INO) == EXT2_FEATURE_COMPAT_RESIZE_INO) { sb.AppendLine("Has online filesystem resize reservations"); } if ((supblk.ftr_compat & EXT2_FEATURE_COMPAT_DIR_INDEX) == EXT2_FEATURE_COMPAT_DIR_INDEX) { sb.AppendLine("Can use hashed indexes on directories"); } if ((supblk.ftr_compat & 0xFFFFFFC0) != 0) { sb.AppendFormat("Unknown compatible features: {0:X8}", supblk.ftr_compat); } sb.AppendLine(); sb.AppendFormat("Compatible features if read-only…:").AppendLine(); if ((supblk.ftr_ro_compat & EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER) == EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER) { sb.AppendLine("Reduced number of superblocks"); } if ((supblk.ftr_ro_compat & EXT2_FEATURE_RO_COMPAT_LARGE_FILE) == EXT2_FEATURE_RO_COMPAT_LARGE_FILE) { sb.AppendLine("Can have files bigger than 2GiB"); } if ((supblk.ftr_ro_compat & EXT2_FEATURE_RO_COMPAT_BTREE_DIR) == EXT2_FEATURE_RO_COMPAT_BTREE_DIR) { sb.AppendLine("Uses B-Tree for directories"); } if ((supblk.ftr_ro_compat & EXT4_FEATURE_RO_COMPAT_HUGE_FILE) == EXT4_FEATURE_RO_COMPAT_HUGE_FILE) { sb.AppendLine("Can have files bigger than 2TiB (ext4)"); } if ((supblk.ftr_ro_compat & EXT4_FEATURE_RO_COMPAT_GDT_CSUM) == EXT4_FEATURE_RO_COMPAT_GDT_CSUM) { sb.AppendLine("Group descriptor checksums and sparse inode table (ext4)"); } if ((supblk.ftr_ro_compat & EXT4_FEATURE_RO_COMPAT_DIR_NLINK) == EXT4_FEATURE_RO_COMPAT_DIR_NLINK) { sb.AppendLine("More than 32000 directory entries (ext4)"); } if ((supblk.ftr_ro_compat & EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE) == EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE) { sb.AppendLine("Supports nanosecond timestamps and creation time (ext4)"); } if ((supblk.ftr_ro_compat & 0xFFFFFF80) != 0) { sb.AppendFormat("Unknown read-only compatible features: {0:X8}", supblk.ftr_ro_compat); } sb.AppendLine(); sb.AppendFormat("Incompatible features…:").AppendLine(); if ((supblk.ftr_incompat & EXT2_FEATURE_INCOMPAT_COMPRESSION) == EXT2_FEATURE_INCOMPAT_COMPRESSION) { sb.AppendLine("Uses compression"); } if ((supblk.ftr_incompat & EXT2_FEATURE_INCOMPAT_FILETYPE) == EXT2_FEATURE_INCOMPAT_FILETYPE) { sb.AppendLine("Filetype in directory entries"); } if ((supblk.ftr_incompat & EXT3_FEATURE_INCOMPAT_RECOVER) == EXT3_FEATURE_INCOMPAT_RECOVER) { sb.AppendLine("Journal needs recovery (ext3)"); } if ((supblk.ftr_incompat & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) == EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) { sb.AppendLine("Has journal on another device (ext3)"); } if ((supblk.ftr_incompat & EXT2_FEATURE_INCOMPAT_META_BG) == EXT2_FEATURE_INCOMPAT_META_BG) { sb.AppendLine("Reduced block group backups"); } if ((supblk.ftr_incompat & EXT4_FEATURE_INCOMPAT_EXTENTS) == EXT4_FEATURE_INCOMPAT_EXTENTS) { sb.AppendLine("Volume use extents (ext4)"); } if ((supblk.ftr_incompat & EXT4_FEATURE_INCOMPAT_64BIT) == EXT4_FEATURE_INCOMPAT_64BIT) { sb.AppendLine("Supports volumes bigger than 2^32 blocks (ext4)"); } if ((supblk.ftr_incompat & EXT4_FEATURE_INCOMPAT_MMP) == EXT4_FEATURE_INCOMPAT_MMP) { sb.AppendLine("Multi-mount protection (ext4)"); } if ((supblk.ftr_incompat & EXT4_FEATURE_INCOMPAT_FLEX_BG) == EXT4_FEATURE_INCOMPAT_FLEX_BG) { sb.AppendLine("Flexible block group metadata location (ext4)"); } if ((supblk.ftr_incompat & EXT4_FEATURE_INCOMPAT_EA_INODE) == EXT4_FEATURE_INCOMPAT_EA_INODE) { sb.AppendLine("Extended attributes can reside in inode (ext4)"); } if ((supblk.ftr_incompat & EXT4_FEATURE_INCOMPAT_DIRDATA) == EXT4_FEATURE_INCOMPAT_DIRDATA) { sb.AppendLine("Data can reside in directory entry (ext4)"); } if ((supblk.ftr_incompat & 0xFFFFF020) != 0) { sb.AppendFormat("Unknown incompatible features: {0:X8}", supblk.ftr_incompat); } information = sb.ToString(); }
public Errno Mount(IMediaImage imagePlugin, Partition partition, Encoding encoding, Dictionary <string, string> options, string @namespace) { Encoding = Encoding.GetEncoding("iso-8859-15"); littleEndian = true; if (options == null) { options = GetDefaultOptions(); } if (options.TryGetValue("debug", out string debugString)) { bool.TryParse(debugString, out debug); } if (imagePlugin.Info.SectorSize < 512) { return(Errno.InvalidArgument); } DicConsole.DebugWriteLine("Xbox FAT plugin", "Reading superblock"); byte[] sector = imagePlugin.ReadSector(partition.Start); superblock = Marshal.ByteArrayToStructureLittleEndian <Superblock>(sector); if (superblock.magic == FATX_CIGAM) { superblock = Marshal.ByteArrayToStructureBigEndian <Superblock>(sector); littleEndian = false; } if (superblock.magic != FATX_MAGIC) { return(Errno.InvalidArgument); } DicConsole.DebugWriteLine("Xbox FAT plugin", littleEndian ? "Filesystem is little endian" : "Filesystem is big endian"); int logicalSectorsPerPhysicalSectors = partition.Offset == 0 && littleEndian ? 8 : 1; DicConsole.DebugWriteLine("Xbox FAT plugin", "logicalSectorsPerPhysicalSectors = {0}", logicalSectorsPerPhysicalSectors); string volumeLabel = StringHandlers.CToString(superblock.volumeLabel, !littleEndian ? Encoding.BigEndianUnicode : Encoding.Unicode, true); XmlFsType = new FileSystemType { Type = "FATX filesystem", ClusterSize = (uint)(superblock.sectorsPerCluster * logicalSectorsPerPhysicalSectors * imagePlugin.Info.SectorSize), VolumeName = volumeLabel, VolumeSerial = $"{superblock.id:X8}" }; XmlFsType.Clusters = (partition.End - partition.Start + 1) * imagePlugin.Info.SectorSize / XmlFsType.ClusterSize; statfs = new FileSystemInfo { Blocks = XmlFsType.Clusters, FilenameLength = MAX_FILENAME, Files = 0, // Requires traversing all directories FreeFiles = 0, Id = { IsInt = true, Serial32 = superblock.magic }, PluginId = Id, Type = littleEndian ? "Xbox FAT" : "Xbox 360 FAT", FreeBlocks = 0 // Requires traversing the FAT }; DicConsole.DebugWriteLine("Xbox FAT plugin", "XmlFsType.ClusterSize: {0}", XmlFsType.ClusterSize); DicConsole.DebugWriteLine("Xbox FAT plugin", "XmlFsType.VolumeName: {0}", XmlFsType.VolumeName); DicConsole.DebugWriteLine("Xbox FAT plugin", "XmlFsType.VolumeSerial: {0}", XmlFsType.VolumeSerial); DicConsole.DebugWriteLine("Xbox FAT plugin", "stat.Blocks: {0}", statfs.Blocks); DicConsole.DebugWriteLine("Xbox FAT plugin", "stat.FilenameLength: {0}", statfs.FilenameLength); DicConsole.DebugWriteLine("Xbox FAT plugin", "stat.Id: {0}", statfs.Id.Serial32); DicConsole.DebugWriteLine("Xbox FAT plugin", "stat.Type: {0}", statfs.Type); byte[] buffer; fatStartSector = FAT_START / imagePlugin.Info.SectorSize + partition.Start; uint fatSize; DicConsole.DebugWriteLine("Xbox FAT plugin", "fatStartSector: {0}", fatStartSector); if (statfs.Blocks > MAX_XFAT16_CLUSTERS) { DicConsole.DebugWriteLine("Xbox FAT plugin", "Reading FAT32"); fatSize = (uint)((statfs.Blocks + 1) * sizeof(uint) / imagePlugin.Info.SectorSize); if ((uint)((statfs.Blocks + 1) * sizeof(uint) % imagePlugin.Info.SectorSize) > 0) { fatSize++; } long fatClusters = fatSize * imagePlugin.Info.SectorSize / 4096; if (fatSize * imagePlugin.Info.SectorSize % 4096 > 0) { fatClusters++; } fatSize = (uint)(fatClusters * 4096 / imagePlugin.Info.SectorSize); DicConsole.DebugWriteLine("Xbox FAT plugin", "FAT is {0} sectors", fatSize); buffer = imagePlugin.ReadSectors(fatStartSector, fatSize); DicConsole.DebugWriteLine("Xbox FAT plugin", "Casting FAT"); fat32 = MemoryMarshal.Cast <byte, uint>(buffer).ToArray(); if (!littleEndian) { for (int i = 0; i < fat32.Length; i++) { fat32[i] = Swapping.Swap(fat32[i]); } } DicConsole.DebugWriteLine("Xbox FAT plugin", "fat32[0] == FATX32_ID = {0}", fat32[0] == FATX32_ID); if (fat32[0] != FATX32_ID) { return(Errno.InvalidArgument); } } else { DicConsole.DebugWriteLine("Xbox FAT plugin", "Reading FAT16"); fatSize = (uint)((statfs.Blocks + 1) * sizeof(ushort) / imagePlugin.Info.SectorSize); if ((uint)((statfs.Blocks + 1) * sizeof(ushort) % imagePlugin.Info.SectorSize) > 0) { fatSize++; } long fatClusters = fatSize * imagePlugin.Info.SectorSize / 4096; if (fatSize * imagePlugin.Info.SectorSize % 4096 > 0) { fatClusters++; } fatSize = (uint)(fatClusters * 4096 / imagePlugin.Info.SectorSize); DicConsole.DebugWriteLine("Xbox FAT plugin", "FAT is {0} sectors", fatSize); buffer = imagePlugin.ReadSectors(fatStartSector, fatSize); DicConsole.DebugWriteLine("Xbox FAT plugin", "Casting FAT"); fat16 = MemoryMarshal.Cast <byte, ushort>(buffer).ToArray(); if (!littleEndian) { for (int i = 0; i < fat16.Length; i++) { fat16[i] = Swapping.Swap(fat16[i]); } } DicConsole.DebugWriteLine("Xbox FAT plugin", "fat16[0] == FATX16_ID = {0}", fat16[0] == FATX16_ID); if (fat16[0] != FATX16_ID) { return(Errno.InvalidArgument); } } sectorsPerCluster = (uint)(superblock.sectorsPerCluster * logicalSectorsPerPhysicalSectors); this.imagePlugin = imagePlugin; firstClusterSector = fatStartSector + fatSize; bytesPerCluster = sectorsPerCluster * imagePlugin.Info.SectorSize; DicConsole.DebugWriteLine("Xbox FAT plugin", "sectorsPerCluster = {0}", sectorsPerCluster); DicConsole.DebugWriteLine("Xbox FAT plugin", "bytesPerCluster = {0}", bytesPerCluster); DicConsole.DebugWriteLine("Xbox FAT plugin", "firstClusterSector = {0}", firstClusterSector); uint[] rootDirectoryClusters = GetClusters(superblock.rootDirectoryCluster); if (rootDirectoryClusters is null) { return(Errno.InvalidArgument); } byte[] rootDirectoryBuffer = new byte[bytesPerCluster * rootDirectoryClusters.Length]; DicConsole.DebugWriteLine("Xbox FAT plugin", "Reading root directory"); for (int i = 0; i < rootDirectoryClusters.Length; i++) { buffer = imagePlugin.ReadSectors(firstClusterSector + (rootDirectoryClusters[i] - 1) * sectorsPerCluster, sectorsPerCluster); Array.Copy(buffer, 0, rootDirectoryBuffer, i * bytesPerCluster, bytesPerCluster); } rootDirectory = new Dictionary <string, DirectoryEntry>(); int pos = 0; while (pos < rootDirectoryBuffer.Length) { DirectoryEntry entry = littleEndian ? Marshal .ByteArrayToStructureLittleEndian <DirectoryEntry >(rootDirectoryBuffer, pos, Marshal.SizeOf <DirectoryEntry>()) : Marshal.ByteArrayToStructureBigEndian <DirectoryEntry>(rootDirectoryBuffer, pos, Marshal .SizeOf < DirectoryEntry >()); pos += Marshal.SizeOf <DirectoryEntry>(); if (entry.filenameSize == UNUSED_DIRENTRY || entry.filenameSize == FINISHED_DIRENTRY) { break; } if (entry.filenameSize == DELETED_DIRENTRY || entry.filenameSize > MAX_FILENAME) { continue; } string filename = Encoding.GetString(entry.filename, 0, entry.filenameSize); rootDirectory.Add(filename, entry); } cultureInfo = new CultureInfo("en-US", false); directoryCache = new Dictionary <string, Dictionary <string, DirectoryEntry> >(); mounted = true; return(Errno.NoError); }
public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, Encoding encoding) { Encoding = encoding ?? Encoding.GetEncoding("iso-8859-15"); information = ""; if (imagePlugin.Info.SectorSize < 512) { return; } uint sbSize = (uint)(Marshal.SizeOf <UNICOS_Superblock>() / imagePlugin.Info.SectorSize); if (Marshal.SizeOf <UNICOS_Superblock>() % imagePlugin.Info.SectorSize != 0) { sbSize++; } byte[] sector = imagePlugin.ReadSectors(partition.Start, sbSize); if (sector.Length < Marshal.SizeOf <UNICOS_Superblock>()) { return; } UNICOS_Superblock unicosSb = Marshal.ByteArrayToStructureBigEndian <UNICOS_Superblock>(sector); if (unicosSb.s_magic != UNICOS_MAGIC) { return; } StringBuilder sb = new StringBuilder(); sb.AppendLine("UNICOS filesystem"); if (unicosSb.s_secure == UNICOS_SECURE) { sb.AppendLine("Volume is secure"); } sb.AppendFormat("Volume contains {0} partitions", unicosSb.s_npart).AppendLine(); sb.AppendFormat("{0} bytes per sector", unicosSb.s_iounit).AppendLine(); sb.AppendLine("4096 bytes per block"); sb.AppendFormat("{0} data blocks in volume", unicosSb.s_fsize).AppendLine(); sb.AppendFormat("Root resides on inode {0}", unicosSb.s_root).AppendLine(); sb.AppendFormat("{0} inodes in volume", unicosSb.s_isize).AppendLine(); sb.AppendFormat("Volume last updated on {0}", DateHandlers.UnixToDateTime(unicosSb.s_time)).AppendLine(); if (unicosSb.s_error > 0) { sb.AppendFormat("Volume is dirty, error code = 0x{0:X16}", unicosSb.s_error).AppendLine(); } sb.AppendFormat("Volume name: {0}", StringHandlers.CToString(unicosSb.s_fname, Encoding)).AppendLine(); information = sb.ToString(); XmlFsType = new FileSystemType { Type = "UNICOS filesystem", ClusterSize = 4096, Clusters = (ulong)unicosSb.s_fsize, VolumeName = StringHandlers.CToString(unicosSb.s_fname, Encoding), ModificationDate = DateHandlers.UnixToDateTime(unicosSb.s_time), ModificationDateSpecified = true }; XmlFsType.Dirty |= unicosSb.s_error > 0; }
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++; } var 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]; if (offset + MAX_LABEL_SIZE > tmp.Length) { break; } Array.Copy(tmp, offset, sector, 0, MAX_LABEL_SIZE); dl = Marshal.ByteArrayToStructureLittleEndian <DiskLabel>(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, 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); } 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); }
public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, Encoding encoding) { Encoding = encoding ?? Encoding.GetEncoding("iso-8859-1"); var sbInformation = new StringBuilder(); XmlFsType = new FileSystemType(); information = null; byte[] bootBlockSectors = imagePlugin.ReadSectors(0 + partition.Start, 2); BootBlock bootBlk = Marshal.ByteArrayToStructureBigEndian <BootBlock>(bootBlockSectors); bootBlk.bootCode = new byte[bootBlockSectors.Length - 12]; Array.Copy(bootBlockSectors, 12, bootBlk.bootCode, 0, bootBlk.bootCode.Length); bootBlockSectors[4] = bootBlockSectors[5] = bootBlockSectors[6] = bootBlockSectors[7] = 0; uint bsum = AmigaBootChecksum(bootBlockSectors); ulong bRootPtr = 0; // If bootblock is correct, let's take its rootblock pointer if (bsum == bootBlk.checksum) { bRootPtr = bootBlk.root_ptr + partition.Start; AaruConsole.DebugWriteLine("AmigaDOS plugin", "Bootblock points to {0} as Rootblock", bRootPtr); } ulong[] rootPtrs = { bRootPtr + partition.Start, ((((partition.End - partition.Start) + 1) / 2) + partition.Start) - 2, ((((partition.End - partition.Start) + 1) / 2) + partition.Start) - 1, (((partition.End - partition.Start) + 1) / 2) + partition.Start, (((partition.End - partition.Start) + 1) / 2) + partition.Start + 4 }; var rootBlk = new RootBlock(); byte[] rootBlockSector = null; bool rootFound = false; uint blockSize = 0; // So to handle even number of sectors foreach (ulong rootPtr in rootPtrs.Where(rootPtr => rootPtr < partition.End && rootPtr >= partition.Start)) { AaruConsole.DebugWriteLine("AmigaDOS plugin", "Searching for Rootblock in sector {0}", rootPtr); rootBlockSector = imagePlugin.ReadSector(rootPtr); rootBlk.type = BigEndianBitConverter.ToUInt32(rootBlockSector, 0x00); AaruConsole.DebugWriteLine("AmigaDOS plugin", "rootBlk.type = {0}", rootBlk.type); if (rootBlk.type != TYPE_HEADER) { continue; } rootBlk.hashTableSize = BigEndianBitConverter.ToUInt32(rootBlockSector, 0x0C); AaruConsole.DebugWriteLine("AmigaDOS plugin", "rootBlk.hashTableSize = {0}", rootBlk.hashTableSize); blockSize = (rootBlk.hashTableSize + 56) * 4; uint sectorsPerBlock = (uint)(blockSize / rootBlockSector.Length); AaruConsole.DebugWriteLine("AmigaDOS plugin", "blockSize = {0}", blockSize); AaruConsole.DebugWriteLine("AmigaDOS plugin", "sectorsPerBlock = {0}", sectorsPerBlock); if (blockSize % rootBlockSector.Length > 0) { sectorsPerBlock++; } if (rootPtr + sectorsPerBlock >= partition.End) { continue; } rootBlockSector = imagePlugin.ReadSectors(rootPtr, sectorsPerBlock); // Clear checksum on sector rootBlk.checksum = BigEndianBitConverter.ToUInt32(rootBlockSector, 20); rootBlockSector[20] = rootBlockSector[21] = rootBlockSector[22] = rootBlockSector[23] = 0; uint rsum = AmigaChecksum(rootBlockSector); AaruConsole.DebugWriteLine("AmigaDOS plugin", "rootBlk.checksum = 0x{0:X8}", rootBlk.checksum); AaruConsole.DebugWriteLine("AmigaDOS plugin", "rsum = 0x{0:X8}", rsum); rootBlk.sec_type = BigEndianBitConverter.ToUInt32(rootBlockSector, rootBlockSector.Length - 4); AaruConsole.DebugWriteLine("AmigaDOS plugin", "rootBlk.sec_type = {0}", rootBlk.sec_type); if (rootBlk.sec_type != SUBTYPE_ROOT || rootBlk.checksum != rsum) { continue; } rootBlockSector = imagePlugin.ReadSectors(rootPtr, sectorsPerBlock); rootFound = true; break; } if (!rootFound) { return; } rootBlk = MarshalRootBlock(rootBlockSector); string diskName = StringHandlers.PascalToString(rootBlk.diskName, Encoding); switch (bootBlk.diskType & 0xFF) { case 0: sbInformation.Append("Amiga Original File System"); XmlFsType.Type = "Amiga OFS"; break; case 1: sbInformation.Append("Amiga Fast File System"); XmlFsType.Type = "Amiga FFS"; break; case 2: sbInformation.Append("Amiga Original File System with international characters"); XmlFsType.Type = "Amiga OFS"; break; case 3: sbInformation.Append("Amiga Fast File System with international characters"); XmlFsType.Type = "Amiga FFS"; break; case 4: sbInformation.Append("Amiga Original File System with directory cache"); XmlFsType.Type = "Amiga OFS"; break; case 5: sbInformation.Append("Amiga Fast File System with directory cache"); XmlFsType.Type = "Amiga FFS"; break; case 6: sbInformation.Append("Amiga Original File System with long filenames"); XmlFsType.Type = "Amiga OFS2"; break; case 7: sbInformation.Append("Amiga Fast File System with long filenames"); XmlFsType.Type = "Amiga FFS2"; break; } if ((bootBlk.diskType & 0x6D754600) == 0x6D754600) { sbInformation.Append(", with multi-user patches"); } sbInformation.AppendLine(); sbInformation.AppendFormat("Volume name: {0}", diskName).AppendLine(); if (bootBlk.checksum == bsum) { var sha1Ctx = new Sha1Context(); sha1Ctx.Update(bootBlk.bootCode); sbInformation.AppendLine("Volume is bootable"); sbInformation.AppendFormat("Boot code SHA1 is {0}", sha1Ctx.End()).AppendLine(); } if (rootBlk.bitmapFlag == 0xFFFFFFFF) { sbInformation.AppendLine("Volume bitmap is valid"); } if (rootBlk.bitmapExtensionBlock != 0x00000000 && rootBlk.bitmapExtensionBlock != 0xFFFFFFFF) { sbInformation.AppendFormat("Bitmap extension at block {0}", rootBlk.bitmapExtensionBlock).AppendLine(); } if ((bootBlk.diskType & 0xFF) == 4 || (bootBlk.diskType & 0xFF) == 5) { sbInformation.AppendFormat("Directory cache starts at block {0}", rootBlk.extension).AppendLine(); } ulong blocks = (((partition.End - partition.Start) + 1) * imagePlugin.Info.SectorSize) / blockSize; sbInformation.AppendFormat("Volume block size is {0} bytes", blockSize).AppendLine(); sbInformation.AppendFormat("Volume has {0} blocks", blocks).AppendLine(); sbInformation.AppendFormat("Volume created on {0}", DateHandlers.AmigaToDateTime(rootBlk.cDays, rootBlk.cMins, rootBlk.cTicks)). AppendLine(); sbInformation.AppendFormat("Volume last modified on {0}", DateHandlers.AmigaToDateTime(rootBlk.vDays, rootBlk.vMins, rootBlk.vTicks)). AppendLine(); sbInformation.AppendFormat("Volume root directory last modified on on {0}", DateHandlers.AmigaToDateTime(rootBlk.rDays, rootBlk.rMins, rootBlk.rTicks)). AppendLine(); sbInformation.AppendFormat("Root block checksum is 0x{0:X8}", rootBlk.checksum).AppendLine(); information = sbInformation.ToString(); XmlFsType.CreationDate = DateHandlers.AmigaToDateTime(rootBlk.cDays, rootBlk.cMins, rootBlk.cTicks); XmlFsType.CreationDateSpecified = true; XmlFsType.ModificationDate = DateHandlers.AmigaToDateTime(rootBlk.vDays, rootBlk.vMins, rootBlk.vTicks); XmlFsType.ModificationDateSpecified = true; XmlFsType.Dirty = rootBlk.bitmapFlag != 0xFFFFFFFF; XmlFsType.Clusters = blocks; XmlFsType.ClusterSize = blockSize; XmlFsType.VolumeName = diskName; XmlFsType.Bootable = bsum == bootBlk.checksum; // Useful as a serial XmlFsType.VolumeSerial = $"{rootBlk.checksum:X8}"; }
// TODO: Find root directory on volumes with DiscRecord // TODO: Support big directories (ADFS-G?) // TODO: Find the real freemap on volumes with DiscRecord, as DiscRecord's discid may be empty but this one isn't public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, Encoding encoding) { Encoding = encoding ?? Encoding.GetEncoding("iso-8859-1"); var sbInformation = new StringBuilder(); XmlFsType = new FileSystemType(); information = ""; ulong sbSector; byte[] sector; uint sectorsToRead; ulong bytes; // ADFS-S, ADFS-M, ADFS-L, ADFS-D without partitions if (partition.Start == 0) { sector = imagePlugin.ReadSector(0); byte oldChk0 = AcornMapChecksum(sector, 255); OldMapSector0 oldMap0 = Marshal.ByteArrayToStructureLittleEndian <OldMapSector0>(sector); sector = imagePlugin.ReadSector(1); byte oldChk1 = AcornMapChecksum(sector, 255); OldMapSector1 oldMap1 = Marshal.ByteArrayToStructureLittleEndian <OldMapSector1>(sector); // According to documentation map1 MUST start on sector 1. On ADFS-D it starts at 0x100, not on sector 1 (0x400) if (oldMap0.checksum == oldChk0 && oldMap1.checksum != oldChk1 && sector.Length >= 512) { sector = imagePlugin.ReadSector(0); byte[] tmp = new byte[256]; Array.Copy(sector, 256, tmp, 0, 256); oldChk1 = AcornMapChecksum(tmp, 255); oldMap1 = Marshal.ByteArrayToStructureLittleEndian <OldMapSector1>(tmp); } if (oldMap0.checksum == oldChk0 && oldMap1.checksum == oldChk1 && oldMap0.checksum != 0 && oldMap1.checksum != 0) { bytes = (ulong)((oldMap0.size[2] << 16) + (oldMap0.size[1] << 8) + oldMap0.size[0]) * 256; byte[] namebytes = new byte[10]; for (int i = 0; i < 5; i++) { namebytes[i * 2] = oldMap0.name[i]; namebytes[(i * 2) + 1] = oldMap1.name[i]; } XmlFsType = new FileSystemType { Bootable = oldMap1.boot != 0, // Or not? Clusters = bytes / imagePlugin.Info.SectorSize, ClusterSize = imagePlugin.Info.SectorSize, Type = "Acorn Advanced Disc Filing System" }; if (ArrayHelpers.ArrayIsNullOrEmpty(namebytes)) { sbSector = OLD_DIRECTORY_LOCATION / imagePlugin.Info.SectorSize; sectorsToRead = OLD_DIRECTORY_SIZE / imagePlugin.Info.SectorSize; if (OLD_DIRECTORY_SIZE % imagePlugin.Info.SectorSize > 0) { sectorsToRead++; } sector = imagePlugin.ReadSectors(sbSector, sectorsToRead); if (sector.Length > OLD_DIRECTORY_SIZE) { byte[] tmp = new byte[OLD_DIRECTORY_SIZE]; Array.Copy(sector, 0, tmp, 0, OLD_DIRECTORY_SIZE - 53); Array.Copy(sector, sector.Length - 54, tmp, OLD_DIRECTORY_SIZE - 54, 53); sector = tmp; } OldDirectory oldRoot = Marshal.ByteArrayToStructureLittleEndian <OldDirectory>(sector); if (oldRoot.header.magic == OLD_DIR_MAGIC && oldRoot.tail.magic == OLD_DIR_MAGIC) { namebytes = oldRoot.tail.name; } else { // RISC OS says the old directory can't be in the new location, hard disks created by RISC OS 3.10 do that... sbSector = NEW_DIRECTORY_LOCATION / imagePlugin.Info.SectorSize; sectorsToRead = NEW_DIRECTORY_SIZE / imagePlugin.Info.SectorSize; if (NEW_DIRECTORY_SIZE % imagePlugin.Info.SectorSize > 0) { sectorsToRead++; } sector = imagePlugin.ReadSectors(sbSector, sectorsToRead); if (sector.Length > OLD_DIRECTORY_SIZE) { byte[] tmp = new byte[OLD_DIRECTORY_SIZE]; Array.Copy(sector, 0, tmp, 0, OLD_DIRECTORY_SIZE - 53); Array.Copy(sector, sector.Length - 54, tmp, OLD_DIRECTORY_SIZE - 54, 53); sector = tmp; } oldRoot = Marshal.ByteArrayToStructureLittleEndian <OldDirectory>(sector); if (oldRoot.header.magic == OLD_DIR_MAGIC && oldRoot.tail.magic == OLD_DIR_MAGIC) { namebytes = oldRoot.tail.name; } else { sector = imagePlugin.ReadSectors(sbSector, sectorsToRead); if (sector.Length > NEW_DIRECTORY_SIZE) { byte[] tmp = new byte[NEW_DIRECTORY_SIZE]; Array.Copy(sector, 0, tmp, 0, NEW_DIRECTORY_SIZE - 41); Array.Copy(sector, sector.Length - 42, tmp, NEW_DIRECTORY_SIZE - 42, 41); sector = tmp; } NewDirectory newRoot = Marshal.ByteArrayToStructureLittleEndian <NewDirectory>(sector); if (newRoot.header.magic == NEW_DIR_MAGIC && newRoot.tail.magic == NEW_DIR_MAGIC) { namebytes = newRoot.tail.title; } } } } sbInformation.AppendLine("Acorn Advanced Disc Filing System"); sbInformation.AppendLine(); sbInformation.AppendFormat("{0} bytes per sector", imagePlugin.Info.SectorSize).AppendLine(); sbInformation.AppendFormat("Volume has {0} bytes", bytes).AppendLine(); sbInformation.AppendFormat("Volume name: {0}", StringHandlers.CToString(namebytes, Encoding)). AppendLine(); if (oldMap1.discId > 0) { XmlFsType.VolumeSerial = $"{oldMap1.discId:X4}"; sbInformation.AppendFormat("Volume ID: {0:X4}", oldMap1.discId).AppendLine(); } if (!ArrayHelpers.ArrayIsNullOrEmpty(namebytes)) { XmlFsType.VolumeName = StringHandlers.CToString(namebytes, Encoding); } information = sbInformation.ToString(); return; } } // Partitioning or not, new formats follow: DiscRecord drSb; sector = imagePlugin.ReadSector(partition.Start); byte newChk = NewMapChecksum(sector); AaruConsole.DebugWriteLine("ADFS Plugin", "newChk = {0}", newChk); AaruConsole.DebugWriteLine("ADFS Plugin", "map.zoneChecksum = {0}", sector[0]); sbSector = BOOT_BLOCK_LOCATION / imagePlugin.Info.SectorSize; sectorsToRead = BOOT_BLOCK_SIZE / imagePlugin.Info.SectorSize; if (BOOT_BLOCK_SIZE % imagePlugin.Info.SectorSize > 0) { sectorsToRead++; } byte[] bootSector = imagePlugin.ReadSectors(sbSector + partition.Start, sectorsToRead); int bootChk = 0; for (int i = 0; i < 0x1FF; i++) { bootChk = (bootChk & 0xFF) + (bootChk >> 8) + bootSector[i]; } AaruConsole.DebugWriteLine("ADFS Plugin", "bootChk = {0}", bootChk); AaruConsole.DebugWriteLine("ADFS Plugin", "bBlock.checksum = {0}", bootSector[0x1FF]); if (newChk == sector[0] && newChk != 0) { NewMap nmap = Marshal.ByteArrayToStructureLittleEndian <NewMap>(sector); drSb = nmap.discRecord; } else if (bootChk == bootSector[0x1FF]) { BootBlock bBlock = Marshal.ByteArrayToStructureLittleEndian <BootBlock>(sector); drSb = bBlock.discRecord; } else { return; } AaruConsole.DebugWriteLine("ADFS Plugin", "drSb.log2secsize = {0}", drSb.log2secsize); AaruConsole.DebugWriteLine("ADFS Plugin", "drSb.spt = {0}", drSb.spt); AaruConsole.DebugWriteLine("ADFS Plugin", "drSb.heads = {0}", drSb.heads); AaruConsole.DebugWriteLine("ADFS Plugin", "drSb.density = {0}", drSb.density); AaruConsole.DebugWriteLine("ADFS Plugin", "drSb.idlen = {0}", drSb.idlen); AaruConsole.DebugWriteLine("ADFS Plugin", "drSb.log2bpmb = {0}", drSb.log2bpmb); AaruConsole.DebugWriteLine("ADFS Plugin", "drSb.skew = {0}", drSb.skew); AaruConsole.DebugWriteLine("ADFS Plugin", "drSb.bootoption = {0}", drSb.bootoption); AaruConsole.DebugWriteLine("ADFS Plugin", "drSb.lowsector = {0}", drSb.lowsector); AaruConsole.DebugWriteLine("ADFS Plugin", "drSb.nzones = {0}", drSb.nzones); AaruConsole.DebugWriteLine("ADFS Plugin", "drSb.zone_spare = {0}", drSb.zone_spare); AaruConsole.DebugWriteLine("ADFS Plugin", "drSb.root = {0}", drSb.root); AaruConsole.DebugWriteLine("ADFS Plugin", "drSb.disc_size = {0}", drSb.disc_size); AaruConsole.DebugWriteLine("ADFS Plugin", "drSb.disc_id = {0}", drSb.disc_id); AaruConsole.DebugWriteLine("ADFS Plugin", "drSb.disc_name = {0}", StringHandlers.CToString(drSb.disc_name, Encoding)); AaruConsole.DebugWriteLine("ADFS Plugin", "drSb.disc_type = {0}", drSb.disc_type); AaruConsole.DebugWriteLine("ADFS Plugin", "drSb.disc_size_high = {0}", drSb.disc_size_high); AaruConsole.DebugWriteLine("ADFS Plugin", "drSb.flags = {0}", drSb.flags); AaruConsole.DebugWriteLine("ADFS Plugin", "drSb.nzones_high = {0}", drSb.nzones_high); AaruConsole.DebugWriteLine("ADFS Plugin", "drSb.format_version = {0}", drSb.format_version); AaruConsole.DebugWriteLine("ADFS Plugin", "drSb.root_size = {0}", drSb.root_size); if (drSb.log2secsize < 8 || drSb.log2secsize > 10) { return; } if (drSb.idlen < drSb.log2secsize + 3 || drSb.idlen > 19) { return; } if (drSb.disc_size_high >> drSb.log2secsize != 0) { return; } if (!ArrayHelpers.ArrayIsNullOrEmpty(drSb.reserved)) { return; } bytes = drSb.disc_size_high; bytes *= 0x100000000; bytes += drSb.disc_size; ulong zones = drSb.nzones_high; zones *= 0x100000000; zones += drSb.nzones; if (bytes > imagePlugin.Info.Sectors * imagePlugin.Info.SectorSize) { return; } XmlFsType = new FileSystemType(); sbInformation.AppendLine("Acorn Advanced Disc Filing System"); sbInformation.AppendLine(); sbInformation.AppendFormat("Version {0}", drSb.format_version).AppendLine(); sbInformation.AppendFormat("{0} bytes per sector", 1 << drSb.log2secsize).AppendLine(); sbInformation.AppendFormat("{0} sectors per track", drSb.spt).AppendLine(); sbInformation.AppendFormat("{0} heads", drSb.heads).AppendLine(); sbInformation.AppendFormat("Density code: {0}", drSb.density).AppendLine(); sbInformation.AppendFormat("Skew: {0}", drSb.skew).AppendLine(); sbInformation.AppendFormat("Boot option: {0}", drSb.bootoption).AppendLine(); // TODO: What the hell is this field refering to? sbInformation.AppendFormat("Root starts at frag {0}", drSb.root).AppendLine(); //sbInformation.AppendFormat("Root is {0} bytes long", drSb.root_size).AppendLine(); sbInformation.AppendFormat("Volume has {0} bytes in {1} zones", bytes, zones).AppendLine(); sbInformation.AppendFormat("Volume flags: 0x{0:X4}", drSb.flags).AppendLine(); if (drSb.disc_id > 0) { XmlFsType.VolumeSerial = $"{drSb.disc_id:X4}"; sbInformation.AppendFormat("Volume ID: {0:X4}", drSb.disc_id).AppendLine(); } if (!ArrayHelpers.ArrayIsNullOrEmpty(drSb.disc_name)) { string discname = StringHandlers.CToString(drSb.disc_name, Encoding); XmlFsType.VolumeName = discname; sbInformation.AppendFormat("Volume name: {0}", discname).AppendLine(); } information = sbInformation.ToString(); XmlFsType.Bootable |= drSb.bootoption != 0; // Or not? XmlFsType.Clusters = bytes / (ulong)(1 << drSb.log2secsize); XmlFsType.ClusterSize = (uint)(1 << drSb.log2secsize); XmlFsType.Type = "Acorn Advanced Disc Filing System"; }
public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, Encoding encoding) { Encoding = encoding ?? Encoding.GetEncoding("iso-8859-15"); information = ""; if (imagePlugin.Info.SectorSize < 512) { return; } EFS_Superblock efsSb = new EFS_Superblock(); // Misaligned if (imagePlugin.Info.XmlMediaType == XmlMediaType.OpticalDisc) { uint sbSize = (uint)((Marshal.SizeOf <EFS_Superblock>() + 0x400) / imagePlugin.Info.SectorSize); if ((Marshal.SizeOf <EFS_Superblock>() + 0x400) % imagePlugin.Info.SectorSize != 0) { sbSize++; } byte[] sector = imagePlugin.ReadSectors(partition.Start, sbSize); if (sector.Length < Marshal.SizeOf <EFS_Superblock>()) { return; } byte[] sbpiece = new byte[Marshal.SizeOf <EFS_Superblock>()]; Array.Copy(sector, 0x200, sbpiece, 0, Marshal.SizeOf <EFS_Superblock>()); efsSb = Marshal.ByteArrayToStructureBigEndian <EFS_Superblock>(sbpiece); DicConsole.DebugWriteLine("EFS plugin", "magic at 0x{0:X3} = 0x{1:X8} (expected 0x{2:X8} or 0x{3:X8})", 0x200, efsSb.sb_magic, EFS_MAGIC, EFS_MAGIC_NEW); } else { uint sbSize = (uint)(Marshal.SizeOf <EFS_Superblock>() / imagePlugin.Info.SectorSize); if (Marshal.SizeOf <EFS_Superblock>() % imagePlugin.Info.SectorSize != 0) { sbSize++; } byte[] sector = imagePlugin.ReadSectors(partition.Start + 1, sbSize); if (sector.Length < Marshal.SizeOf <EFS_Superblock>()) { return; } efsSb = Marshal.ByteArrayToStructureBigEndian <EFS_Superblock>(sector); DicConsole.DebugWriteLine("EFS plugin", "magic at {0} = 0x{1:X8} (expected 0x{2:X8} or 0x{3:X8})", 1, efsSb.sb_magic, EFS_MAGIC, EFS_MAGIC_NEW); } if (efsSb.sb_magic != EFS_MAGIC && efsSb.sb_magic != EFS_MAGIC_NEW) { return; } StringBuilder sb = new StringBuilder(); sb.AppendLine("SGI extent filesystem"); if (efsSb.sb_magic == EFS_MAGIC_NEW) { sb.AppendLine("New version"); } sb.AppendFormat("Filesystem size: {0} basic blocks", efsSb.sb_size).AppendLine(); sb.AppendFormat("First cylinder group starts at block {0}", efsSb.sb_firstcg).AppendLine(); sb.AppendFormat("Cylinder group size: {0} basic blocks", efsSb.sb_cgfsize).AppendLine(); sb.AppendFormat("{0} inodes per cylinder group", efsSb.sb_cgisize).AppendLine(); sb.AppendFormat("{0} sectors per track", efsSb.sb_sectors).AppendLine(); sb.AppendFormat("{0} heads per cylinder", efsSb.sb_heads).AppendLine(); sb.AppendFormat("{0} cylinder groups", efsSb.sb_ncg).AppendLine(); sb.AppendFormat("Volume created on {0}", DateHandlers.UnixToDateTime(efsSb.sb_time)).AppendLine(); sb.AppendFormat("{0} bytes on bitmap", efsSb.sb_bmsize).AppendLine(); sb.AppendFormat("{0} free blocks", efsSb.sb_tfree).AppendLine(); sb.AppendFormat("{0} free inodes", efsSb.sb_tinode).AppendLine(); if (efsSb.sb_bmblock > 0) { sb.AppendFormat("Bitmap resides at block {0}", efsSb.sb_bmblock).AppendLine(); } if (efsSb.sb_replsb > 0) { sb.AppendFormat("Replacement superblock resides at block {0}", efsSb.sb_replsb).AppendLine(); } if (efsSb.sb_lastinode > 0) { sb.AppendFormat("Last inode allocated: {0}", efsSb.sb_lastinode).AppendLine(); } if (efsSb.sb_dirty > 0) { sb.AppendLine("Volume is dirty"); } sb.AppendFormat("Checksum: 0x{0:X8}", efsSb.sb_checksum).AppendLine(); sb.AppendFormat("Volume name: {0}", StringHandlers.CToString(efsSb.sb_fname, Encoding)).AppendLine(); sb.AppendFormat("Volume pack: {0}", StringHandlers.CToString(efsSb.sb_fpack, Encoding)).AppendLine(); information = sb.ToString(); XmlFsType = new FileSystemType { Type = "Extent File System", ClusterSize = 512, Clusters = (ulong)efsSb.sb_size, FreeClusters = (ulong)efsSb.sb_tfree, FreeClustersSpecified = true, Dirty = efsSb.sb_dirty > 0, VolumeName = StringHandlers.CToString(efsSb.sb_fname, Encoding), VolumeSerial = $"{efsSb.sb_checksum:X8}", CreationDate = DateHandlers.UnixToDateTime(efsSb.sb_time), CreationDateSpecified = true }; }
// TODO: BBC Master hard disks are untested... public bool Identify(IMediaImage imagePlugin, Partition partition) { if (partition.Start >= partition.End) { return(false); } ulong sbSector; uint sectorsToRead; if (imagePlugin.Info.SectorSize < 256) { return(false); } byte[] sector; // ADFS-S, ADFS-M, ADFS-L, ADFS-D without partitions if (partition.Start == 0) { sector = imagePlugin.ReadSector(0); byte oldChk0 = AcornMapChecksum(sector, 255); OldMapSector0 oldMap0 = Marshal.ByteArrayToStructureLittleEndian <OldMapSector0>(sector); sector = imagePlugin.ReadSector(1); byte oldChk1 = AcornMapChecksum(sector, 255); OldMapSector1 oldMap1 = Marshal.ByteArrayToStructureLittleEndian <OldMapSector1>(sector); AaruConsole.DebugWriteLine("ADFS Plugin", "oldMap0.checksum = {0}", oldMap0.checksum); AaruConsole.DebugWriteLine("ADFS Plugin", "oldChk0 = {0}", oldChk0); // According to documentation map1 MUST start on sector 1. On ADFS-D it starts at 0x100, not on sector 1 (0x400) if (oldMap0.checksum == oldChk0 && oldMap1.checksum != oldChk1 && sector.Length >= 512) { sector = imagePlugin.ReadSector(0); byte[] tmp = new byte[256]; Array.Copy(sector, 256, tmp, 0, 256); oldChk1 = AcornMapChecksum(tmp, 255); oldMap1 = Marshal.ByteArrayToStructureLittleEndian <OldMapSector1>(tmp); } AaruConsole.DebugWriteLine("ADFS Plugin", "oldMap1.checksum = {0}", oldMap1.checksum); AaruConsole.DebugWriteLine("ADFS Plugin", "oldChk1 = {0}", oldChk1); if (oldMap0.checksum == oldChk0 && oldMap1.checksum == oldChk1 && oldMap0.checksum != 0 && oldMap1.checksum != 0) { sbSector = OLD_DIRECTORY_LOCATION / imagePlugin.Info.SectorSize; sectorsToRead = OLD_DIRECTORY_SIZE / imagePlugin.Info.SectorSize; if (OLD_DIRECTORY_SIZE % imagePlugin.Info.SectorSize > 0) { sectorsToRead++; } sector = imagePlugin.ReadSectors(sbSector, sectorsToRead); if (sector.Length > OLD_DIRECTORY_SIZE) { byte[] tmp = new byte[OLD_DIRECTORY_SIZE]; Array.Copy(sector, 0, tmp, 0, OLD_DIRECTORY_SIZE - 53); Array.Copy(sector, sector.Length - 54, tmp, OLD_DIRECTORY_SIZE - 54, 53); sector = tmp; } OldDirectory oldRoot = Marshal.ByteArrayToStructureLittleEndian <OldDirectory>(sector); byte dirChk = AcornDirectoryChecksum(sector, (int)OLD_DIRECTORY_SIZE - 1); AaruConsole.DebugWriteLine("ADFS Plugin", "oldRoot.header.magic at 0x200 = {0}", oldRoot.header.magic); AaruConsole.DebugWriteLine("ADFS Plugin", "oldRoot.tail.magic at 0x200 = {0}", oldRoot.tail.magic); AaruConsole.DebugWriteLine("ADFS Plugin", "oldRoot.tail.checkByte at 0x200 = {0}", oldRoot.tail.checkByte); AaruConsole.DebugWriteLine("ADFS Plugin", "dirChk at 0x200 = {0}", dirChk); if ((oldRoot.header.magic == OLD_DIR_MAGIC && oldRoot.tail.magic == OLD_DIR_MAGIC) || (oldRoot.header.magic == NEW_DIR_MAGIC && oldRoot.tail.magic == NEW_DIR_MAGIC)) { return(true); } // RISC OS says the old directory can't be in the new location, hard disks created by RISC OS 3.10 do that... sbSector = NEW_DIRECTORY_LOCATION / imagePlugin.Info.SectorSize; sectorsToRead = NEW_DIRECTORY_SIZE / imagePlugin.Info.SectorSize; if (NEW_DIRECTORY_SIZE % imagePlugin.Info.SectorSize > 0) { sectorsToRead++; } sector = imagePlugin.ReadSectors(sbSector, sectorsToRead); if (sector.Length > OLD_DIRECTORY_SIZE) { byte[] tmp = new byte[OLD_DIRECTORY_SIZE]; Array.Copy(sector, 0, tmp, 0, OLD_DIRECTORY_SIZE - 53); Array.Copy(sector, sector.Length - 54, tmp, OLD_DIRECTORY_SIZE - 54, 53); sector = tmp; } oldRoot = Marshal.ByteArrayToStructureLittleEndian <OldDirectory>(sector); dirChk = AcornDirectoryChecksum(sector, (int)OLD_DIRECTORY_SIZE - 1); AaruConsole.DebugWriteLine("ADFS Plugin", "oldRoot.header.magic at 0x400 = {0}", oldRoot.header.magic); AaruConsole.DebugWriteLine("ADFS Plugin", "oldRoot.tail.magic at 0x400 = {0}", oldRoot.tail.magic); AaruConsole.DebugWriteLine("ADFS Plugin", "oldRoot.tail.checkByte at 0x400 = {0}", oldRoot.tail.checkByte); AaruConsole.DebugWriteLine("ADFS Plugin", "dirChk at 0x400 = {0}", dirChk); if ((oldRoot.header.magic == OLD_DIR_MAGIC && oldRoot.tail.magic == OLD_DIR_MAGIC) || (oldRoot.header.magic == NEW_DIR_MAGIC && oldRoot.tail.magic == NEW_DIR_MAGIC)) { return(true); } } } // Partitioning or not, new formats follow: DiscRecord drSb; sector = imagePlugin.ReadSector(partition.Start); byte newChk = NewMapChecksum(sector); AaruConsole.DebugWriteLine("ADFS Plugin", "newChk = {0}", newChk); AaruConsole.DebugWriteLine("ADFS Plugin", "map.zoneChecksum = {0}", sector[0]); sbSector = BOOT_BLOCK_LOCATION / imagePlugin.Info.SectorSize; sectorsToRead = BOOT_BLOCK_SIZE / imagePlugin.Info.SectorSize; if (BOOT_BLOCK_SIZE % imagePlugin.Info.SectorSize > 0) { sectorsToRead++; } if (sbSector + partition.Start + sectorsToRead >= partition.End) { return(false); } byte[] bootSector = imagePlugin.ReadSectors(sbSector + partition.Start, sectorsToRead); int bootChk = 0; if (bootSector.Length < 512) { return(false); } for (int i = 0; i < 0x1FF; i++) { bootChk = (bootChk & 0xFF) + (bootChk >> 8) + bootSector[i]; } AaruConsole.DebugWriteLine("ADFS Plugin", "bootChk = {0}", bootChk); AaruConsole.DebugWriteLine("ADFS Plugin", "bBlock.checksum = {0}", bootSector[0x1FF]); if (newChk == sector[0] && newChk != 0) { NewMap nmap = Marshal.ByteArrayToStructureLittleEndian <NewMap>(sector); drSb = nmap.discRecord; } else if (bootChk == bootSector[0x1FF]) { BootBlock bBlock = Marshal.ByteArrayToStructureLittleEndian <BootBlock>(bootSector); drSb = bBlock.discRecord; } else { return(false); } AaruConsole.DebugWriteLine("ADFS Plugin", "drSb.log2secsize = {0}", drSb.log2secsize); AaruConsole.DebugWriteLine("ADFS Plugin", "drSb.idlen = {0}", drSb.idlen); AaruConsole.DebugWriteLine("ADFS Plugin", "drSb.disc_size_high = {0}", drSb.disc_size_high); AaruConsole.DebugWriteLine("ADFS Plugin", "drSb.disc_size = {0}", drSb.disc_size); AaruConsole.DebugWriteLine("ADFS Plugin", "IsNullOrEmpty(drSb.reserved) = {0}", ArrayHelpers.ArrayIsNullOrEmpty(drSb.reserved)); if (drSb.log2secsize < 8 || drSb.log2secsize > 10) { return(false); } if (drSb.idlen < drSb.log2secsize + 3 || drSb.idlen > 19) { return(false); } if (drSb.disc_size_high >> drSb.log2secsize != 0) { return(false); } if (!ArrayHelpers.ArrayIsNullOrEmpty(drSb.reserved)) { return(false); } ulong bytes = drSb.disc_size_high; bytes *= 0x100000000; bytes += drSb.disc_size; return(bytes <= imagePlugin.Info.Sectors * imagePlugin.Info.SectorSize); }
public void Convert() { Environment.CurrentDirectory = DataFolder; Resume resume = null; CICMMetadataType sidecar = null; var filtersList = new FiltersList(); IFilter inputFilter = filtersList.GetFilter(InputPath); Assert.IsNotNull(inputFilter, "Cannot open specified file."); string outputPath = Path.Combine(Path.GetTempPath(), SuggestedOutputFilename); Assert.IsFalse(File.Exists(outputPath), "Output file already exists, not continuing."); IMediaImage inputFormat = ImageFormat.Detect(inputFilter); Assert.IsNotNull(inputFormat, "Input image format not identified, not proceeding with conversion."); Assert.IsTrue(inputFormat.Open(inputFilter), "Unable to open image format"); Assert.IsTrue(OutputFormat.SupportedMediaTypes.Contains(inputFormat.Info.MediaType), "Output format does not support media type, cannot continue..."); if (inputFormat.Info.ReadableSectorTags.Count == 0) { Assert.IsFalse(UseLong, "Input image does not support long sectors."); } var inputOptical = inputFormat as IOpticalMediaImage; var outputOptical = OutputFormat as IWritableOpticalImage; Assert.IsNotNull(inputOptical, "Could not treat existing image as optical disc."); Assert.IsNotNull(outputOptical, "Could not treat new image as optical disc."); Assert.IsNotNull(inputOptical.Tracks, "Existing image contains no tracks."); Assert.IsTrue(outputOptical.Create(outputPath, inputFormat.Info.MediaType, ParsedOptions, inputFormat.Info.Sectors, inputFormat.Info.SectorSize), $"Error {outputOptical.ErrorMessage} creating output image."); var metadata = new ImageInfo { Application = "Aaru", ApplicationVersion = Version.GetVersion(), Comments = inputFormat.Info.Comments, Creator = inputFormat.Info.Creator, DriveFirmwareRevision = inputFormat.Info.DriveFirmwareRevision, DriveManufacturer = inputFormat.Info.DriveManufacturer, DriveModel = inputFormat.Info.DriveModel, DriveSerialNumber = inputFormat.Info.DriveSerialNumber, LastMediaSequence = inputFormat.Info.LastMediaSequence, MediaBarcode = inputFormat.Info.MediaBarcode, MediaManufacturer = inputFormat.Info.MediaManufacturer, MediaModel = inputFormat.Info.MediaModel, MediaPartNumber = inputFormat.Info.MediaPartNumber, MediaSequence = inputFormat.Info.MediaSequence, MediaSerialNumber = inputFormat.Info.MediaSerialNumber, MediaTitle = inputFormat.Info.MediaTitle }; Assert.IsTrue(outputOptical.SetMetadata(metadata), $"Error {outputOptical.ErrorMessage} setting metadata, "); CICMMetadataType cicmMetadata = inputFormat.CicmMetadata; List <DumpHardwareType> dumpHardware = inputFormat.DumpHardware; foreach (MediaTagType mediaTag in inputFormat.Info.ReadableMediaTags.Where(mediaTag => outputOptical.SupportedMediaTags.Contains(mediaTag))) { AaruConsole.WriteLine("Converting media tag {0}", mediaTag); byte[] tag = inputFormat.ReadDiskTag(mediaTag); Assert.IsTrue(outputOptical.WriteMediaTag(tag, mediaTag)); } AaruConsole.WriteLine("{0} sectors to convert", inputFormat.Info.Sectors); ulong doneSectors; Assert.IsTrue(outputOptical.SetTracks(inputOptical.Tracks), $"Error {outputOptical.ErrorMessage} sending tracks list to output image."); foreach (Track track in inputOptical.Tracks) { doneSectors = 0; ulong trackSectors = track.TrackEndSector - track.TrackStartSector + 1; while (doneSectors < trackSectors) { byte[] sector; uint sectorsToDo; if (trackSectors - doneSectors >= SECTORS_TO_READ) { sectorsToDo = SECTORS_TO_READ; } else { sectorsToDo = (uint)(trackSectors - doneSectors); } bool useNotLong = false; bool result = false; if (UseLong) { if (sectorsToDo == 1) { sector = inputFormat.ReadSectorLong(doneSectors + track.TrackStartSector); result = outputOptical.WriteSectorLong(sector, doneSectors + track.TrackStartSector); } else { sector = inputFormat.ReadSectorsLong(doneSectors + track.TrackStartSector, sectorsToDo); result = outputOptical.WriteSectorsLong(sector, doneSectors + track.TrackStartSector, sectorsToDo); } if (!result && sector.Length % 2352 != 0) { useNotLong = true; } } if (!UseLong || useNotLong) { if (sectorsToDo == 1) { sector = inputFormat.ReadSector(doneSectors + track.TrackStartSector); result = outputOptical.WriteSector(sector, doneSectors + track.TrackStartSector); } else { sector = inputFormat.ReadSectors(doneSectors + track.TrackStartSector, sectorsToDo); result = outputOptical.WriteSectors(sector, doneSectors + track.TrackStartSector, sectorsToDo); } } Assert.IsTrue(result, $"Error {outputOptical.ErrorMessage} writing sector {doneSectors + track.TrackStartSector}, not continuing..."); doneSectors += sectorsToDo; } } Dictionary <byte, string> isrcs = new Dictionary <byte, string>(); Dictionary <byte, byte> trackFlags = new Dictionary <byte, byte>(); string mcn = null; HashSet <int> subchannelExtents = new HashSet <int>(); Dictionary <byte, int> smallestPregapLbaPerTrack = new Dictionary <byte, int>(); Track[] tracks = new Track[inputOptical.Tracks.Count]; for (int i = 0; i < tracks.Length; i++) { tracks[i] = new Track { Indexes = new Dictionary <ushort, int>(), TrackDescription = inputOptical.Tracks[i].TrackDescription, TrackEndSector = inputOptical.Tracks[i].TrackEndSector, TrackStartSector = inputOptical.Tracks[i].TrackStartSector, TrackPregap = inputOptical.Tracks[i].TrackPregap, TrackSequence = inputOptical.Tracks[i].TrackSequence, TrackSession = inputOptical.Tracks[i].TrackSession, TrackBytesPerSector = inputOptical.Tracks[i].TrackBytesPerSector, TrackRawBytesPerSector = inputOptical.Tracks[i].TrackRawBytesPerSector, TrackType = inputOptical.Tracks[i].TrackType, TrackSubchannelType = inputOptical.Tracks[i].TrackSubchannelType }; foreach (KeyValuePair <ushort, int> idx in inputOptical.Tracks[i].Indexes) { tracks[i].Indexes[idx.Key] = idx.Value; } } foreach (SectorTagType tag in inputFormat.Info.ReadableSectorTags.Where(t => t == SectorTagType.CdTrackIsrc). OrderBy(t => t)) { foreach (Track track in tracks) { byte[] isrc = inputFormat.ReadSectorTag(track.TrackSequence, tag); if (isrc is null) { continue; } isrcs[(byte)track.TrackSequence] = Encoding.UTF8.GetString(isrc); } } foreach (SectorTagType tag in inputFormat.Info.ReadableSectorTags. Where(t => t == SectorTagType.CdTrackFlags).OrderBy(t => t)) { foreach (Track track in tracks) { byte[] flags = inputFormat.ReadSectorTag(track.TrackSequence, tag); if (flags is null) { continue; } trackFlags[(byte)track.TrackSequence] = flags[0]; } } for (ulong s = 0; s < inputFormat.Info.Sectors; s++) { if (s > int.MaxValue) { break; } subchannelExtents.Add((int)s); } foreach (SectorTagType tag in inputFormat.Info.ReadableSectorTags.OrderBy(t => t).TakeWhile(tag => UseLong)) { switch (tag) { case SectorTagType.AppleSectorTag: case SectorTagType.CdSectorSync: case SectorTagType.CdSectorHeader: case SectorTagType.CdSectorSubHeader: case SectorTagType.CdSectorEdc: case SectorTagType.CdSectorEccP: case SectorTagType.CdSectorEccQ: case SectorTagType.CdSectorEcc: // This tags are inline in long sector continue; } if (!outputOptical.SupportedSectorTags.Contains(tag)) { continue; } foreach (Track track in inputOptical.Tracks) { doneSectors = 0; ulong trackSectors = track.TrackEndSector - track.TrackStartSector + 1; byte[] sector; bool result; switch (tag) { case SectorTagType.CdTrackFlags: case SectorTagType.CdTrackIsrc: sector = inputFormat.ReadSectorTag(track.TrackSequence, tag); result = outputOptical.WriteSectorTag(sector, track.TrackSequence, tag); Assert.IsTrue(result, $"Error {outputOptical.ErrorMessage} writing tag, not continuing..."); continue; } while (doneSectors < trackSectors) { uint sectorsToDo; if (trackSectors - doneSectors >= SECTORS_TO_READ) { sectorsToDo = SECTORS_TO_READ; } else { sectorsToDo = (uint)(trackSectors - doneSectors); } if (sectorsToDo == 1) { sector = inputFormat.ReadSectorTag(doneSectors + track.TrackStartSector, tag); if (tag == SectorTagType.CdSectorSubchannel) { bool indexesChanged = CompactDisc.WriteSubchannelToImage(MmcSubchannel.Raw, MmcSubchannel.Raw, sector, doneSectors + track.TrackStartSector, 1, null, isrcs, (byte)track.TrackSequence, ref mcn, tracks, subchannelExtents, true, outputOptical, true, true, null, null, smallestPregapLbaPerTrack, false); if (indexesChanged) { outputOptical.SetTracks(tracks.ToList()); } result = true; } else { result = outputOptical.WriteSectorTag(sector, doneSectors + track.TrackStartSector, tag); } } else { sector = inputFormat.ReadSectorsTag(doneSectors + track.TrackStartSector, sectorsToDo, tag); if (tag == SectorTagType.CdSectorSubchannel) { bool indexesChanged = CompactDisc.WriteSubchannelToImage(MmcSubchannel.Raw, MmcSubchannel.Raw, sector, doneSectors + track.TrackStartSector, sectorsToDo, null, isrcs, (byte)track.TrackSequence, ref mcn, tracks, subchannelExtents, true, outputOptical, true, true, null, null, smallestPregapLbaPerTrack, false); if (indexesChanged) { outputOptical.SetTracks(tracks.ToList()); } result = true; } else { result = outputOptical.WriteSectorsTag(sector, doneSectors + track.TrackStartSector, sectorsToDo, tag); } } Assert.IsTrue(result, $"Error {outputOptical.ErrorMessage} writing tag for sector {doneSectors + track.TrackStartSector}, not continuing..."); doneSectors += sectorsToDo; } } } if (isrcs.Count > 0) { foreach (KeyValuePair <byte, string> isrc in isrcs) { outputOptical.WriteSectorTag(Encoding.UTF8.GetBytes(isrc.Value), isrc.Key, SectorTagType.CdTrackIsrc); } } if (trackFlags.Count > 0) { foreach ((byte track, byte flags) in trackFlags) { outputOptical.WriteSectorTag(new[] { flags }, track, SectorTagType.CdTrackFlags); } } if (mcn != null) { outputOptical.WriteMediaTag(Encoding.UTF8.GetBytes(mcn), MediaTagType.CD_MCN); } if (resume != null || dumpHardware != null) { if (resume != null) { outputOptical.SetDumpHardware(resume.Tries); } else if (dumpHardware != null) { outputOptical.SetDumpHardware(dumpHardware); } } if (sidecar != null || cicmMetadata != null) { if (sidecar != null) { outputOptical.SetCicmMetadata(sidecar); } else if (cicmMetadata != null) { outputOptical.SetCicmMetadata(cicmMetadata); } } Assert.True(outputOptical.Close(), $"Error {outputOptical.ErrorMessage} closing output image... Contents are not correct."); // Some images will never generate the same if (Md5 != null) { string md5 = Md5Context.File(outputPath, out _); Assert.AreEqual(Md5, md5, "Hashes are different"); } File.Delete(outputPath); }
public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, Encoding encoding) { Encoding = Encoding.UTF8; information = ""; uint sbSize = (uint)(Marshal.SizeOf <RefsVolumeHeader>() / imagePlugin.Info.SectorSize); if (Marshal.SizeOf <RefsVolumeHeader>() % imagePlugin.Info.SectorSize != 0) { sbSize++; } if (partition.Start + sbSize >= partition.End) { return; } byte[] sector = imagePlugin.ReadSectors(partition.Start, sbSize); if (sector.Length < Marshal.SizeOf <RefsVolumeHeader>()) { return; } RefsVolumeHeader refsVhdr = Marshal.ByteArrayToStructureLittleEndian <RefsVolumeHeader>(sector); DicConsole.DebugWriteLine("ReFS plugin", "VolumeHeader.jump empty? = {0}", ArrayHelpers.ArrayIsNullOrEmpty(refsVhdr.jump)); DicConsole.DebugWriteLine("ReFS plugin", "VolumeHeader.signature = {0}", StringHandlers.CToString(refsVhdr.signature)); DicConsole.DebugWriteLine("ReFS plugin", "VolumeHeader.mustBeZero empty? = {0}", ArrayHelpers.ArrayIsNullOrEmpty(refsVhdr.mustBeZero)); DicConsole.DebugWriteLine("ReFS plugin", "VolumeHeader.identifier = {0}", StringHandlers.CToString(BitConverter.GetBytes(refsVhdr.identifier))); DicConsole.DebugWriteLine("ReFS plugin", "VolumeHeader.length = {0}", refsVhdr.length); DicConsole.DebugWriteLine("ReFS plugin", "VolumeHeader.checksum = 0x{0:X4}", refsVhdr.checksum); DicConsole.DebugWriteLine("ReFS plugin", "VolumeHeader.sectors = {0}", refsVhdr.sectors); DicConsole.DebugWriteLine("ReFS plugin", "VolumeHeader.bytesPerSector = {0}", refsVhdr.bytesPerSector); DicConsole.DebugWriteLine("ReFS plugin", "VolumeHeader.sectorsPerCluster = {0}", refsVhdr.sectorsPerCluster); DicConsole.DebugWriteLine("ReFS plugin", "VolumeHeader.unknown1 zero? = {0}", refsVhdr.unknown1 == 0); DicConsole.DebugWriteLine("ReFS plugin", "VolumeHeader.unknown2 zero? = {0}", refsVhdr.unknown2 == 0); DicConsole.DebugWriteLine("ReFS plugin", "VolumeHeader.unknown3 zero? = {0}", refsVhdr.unknown3 == 0); DicConsole.DebugWriteLine("ReFS plugin", "VolumeHeader.unknown4 zero? = {0}", refsVhdr.unknown4 == 0); DicConsole.DebugWriteLine("ReFS plugin", "VolumeHeader.unknown5 empty? = {0}", ArrayHelpers.ArrayIsNullOrEmpty(refsVhdr.unknown5)); if (refsVhdr.identifier != FSRS || !ArrayHelpers.ArrayIsNullOrEmpty(refsVhdr.mustBeZero) || !refsVhdr.signature.SequenceEqual(refsSignature)) { return; } StringBuilder sb = new StringBuilder(); sb.AppendLine("Microsoft Resilient File System"); sb.AppendFormat("Volume uses {0} bytes per sector", refsVhdr.bytesPerSector).AppendLine(); sb.AppendFormat("Volume uses {0} sectors per cluster ({1} bytes)", refsVhdr.sectorsPerCluster, refsVhdr.sectorsPerCluster * refsVhdr.bytesPerSector).AppendLine(); sb.AppendFormat("Volume has {0} sectors ({1} bytes)", refsVhdr.sectors, refsVhdr.sectors * refsVhdr.bytesPerSector).AppendLine(); information = sb.ToString(); XmlFsType = new FileSystemType { Type = "Resilient File System", ClusterSize = refsVhdr.bytesPerSector * refsVhdr.sectorsPerCluster, Clusters = refsVhdr.sectors / refsVhdr.sectorsPerCluster }; }
public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, Encoding encoding) { Encoding = encoding ?? Encoding.GetEncoding("shift_jis"); var sbInformation = new StringBuilder(); information = ""; XmlFsType = new FileSystemType(); var fields = new NintendoFields(); byte[] header = imagePlugin.ReadSectors(0, 0x50000 / imagePlugin.Info.SectorSize); bool wii = false; uint magicGc = BigEndianBitConverter.ToUInt32(header, 0x1C); uint magicWii = BigEndianBitConverter.ToUInt32(header, 0x18); if (magicWii == 0x5D1C9EA3) { wii = true; } else if (magicGc != 0xC2339F3D) { return; } fields.DiscType = Encoding.ASCII.GetString(header, 0, 1); fields.GameCode = Encoding.ASCII.GetString(header, 1, 2); fields.RegionCode = Encoding.ASCII.GetString(header, 3, 1); fields.PublisherCode = Encoding.ASCII.GetString(header, 4, 2); fields.DiscId = Encoding.ASCII.GetString(header, 0, 6); fields.DiscNumber = header[6]; fields.DiscVersion = header[7]; fields.Streaming |= header[8] > 0; fields.StreamBufferSize = header[9]; byte[] temp = new byte[64]; Array.Copy(header, 0x20, temp, 0, 64); fields.Title = StringHandlers.CToString(temp, Encoding); if (!wii) { fields.DebugOff = BigEndianBitConverter.ToUInt32(header, 0x0400); fields.DebugAddr = BigEndianBitConverter.ToUInt32(header, 0x0404); fields.DolOff = BigEndianBitConverter.ToUInt32(header, 0x0420); fields.FstOff = BigEndianBitConverter.ToUInt32(header, 0x0424); fields.FstSize = BigEndianBitConverter.ToUInt32(header, 0x0428); fields.FstMax = BigEndianBitConverter.ToUInt32(header, 0x042C); } if (wii) { uint offset1 = BigEndianBitConverter.ToUInt32(header, 0x40004) << 2; uint offset2 = BigEndianBitConverter.ToUInt32(header, 0x4000C) << 2; uint offset3 = BigEndianBitConverter.ToUInt32(header, 0x40014) << 2; uint offset4 = BigEndianBitConverter.ToUInt32(header, 0x4001C) << 2; fields.FirstPartitions = new NintendoPartition[BigEndianBitConverter.ToUInt32(header, 0x40000)]; fields.SecondPartitions = new NintendoPartition[BigEndianBitConverter.ToUInt32(header, 0x40008)]; fields.ThirdPartitions = new NintendoPartition[BigEndianBitConverter.ToUInt32(header, 0x40010)]; fields.FourthPartitions = new NintendoPartition[BigEndianBitConverter.ToUInt32(header, 0x40018)]; for (int i = 0; i < fields.FirstPartitions.Length; i++) { if (offset1 + (i * 8) + 8 < 0x50000) { fields.FirstPartitions[i].Offset = BigEndianBitConverter.ToUInt32(header, (int)(offset1 + (i * 8) + 0)) << 2; fields.FirstPartitions[i].Type = BigEndianBitConverter.ToUInt32(header, (int)(offset1 + (i * 8) + 4)); } } for (int i = 0; i < fields.SecondPartitions.Length; i++) { if (offset1 + (i * 8) + 8 < 0x50000) { fields.FirstPartitions[i].Offset = BigEndianBitConverter.ToUInt32(header, (int)(offset2 + (i * 8) + 0)) << 2; fields.FirstPartitions[i].Type = BigEndianBitConverter.ToUInt32(header, (int)(offset2 + (i * 8) + 4)); } } for (int i = 0; i < fields.ThirdPartitions.Length; i++) { if (offset1 + (i * 8) + 8 < 0x50000) { fields.FirstPartitions[i].Offset = BigEndianBitConverter.ToUInt32(header, (int)(offset3 + (i * 8) + 0)) << 2; fields.FirstPartitions[i].Type = BigEndianBitConverter.ToUInt32(header, (int)(offset3 + (i * 8) + 4)); } } for (int i = 0; i < fields.FourthPartitions.Length; i++) { if (offset1 + (i * 8) + 8 < 0x50000) { fields.FirstPartitions[i].Offset = BigEndianBitConverter.ToUInt32(header, (int)(offset4 + (i * 8) + 0)) << 2; fields.FirstPartitions[i].Type = BigEndianBitConverter.ToUInt32(header, (int)(offset4 + (i * 8) + 4)); } } fields.Region = header[0x4E000]; fields.JapanAge = header[0x4E010]; fields.UsaAge = header[0x4E011]; fields.GermanAge = header[0x4E013]; fields.PegiAge = header[0x4E014]; fields.FinlandAge = header[0x4E015]; fields.PortugalAge = header[0x4E016]; fields.UkAge = header[0x4E017]; fields.AustraliaAge = header[0x4E018]; fields.KoreaAge = header[0x4E019]; } else { fields.FirstPartitions = new NintendoPartition[0]; fields.SecondPartitions = new NintendoPartition[0]; fields.ThirdPartitions = new NintendoPartition[0]; fields.FourthPartitions = new NintendoPartition[0]; } AaruConsole.DebugWriteLine("Nintendo plugin", "discType = {0}", fields.DiscType); AaruConsole.DebugWriteLine("Nintendo plugin", "gameCode = {0}", fields.GameCode); AaruConsole.DebugWriteLine("Nintendo plugin", "regionCode = {0}", fields.RegionCode); AaruConsole.DebugWriteLine("Nintendo plugin", "publisherCode = {0}", fields.PublisherCode); AaruConsole.DebugWriteLine("Nintendo plugin", "discID = {0}", fields.DiscId); AaruConsole.DebugWriteLine("Nintendo plugin", "discNumber = {0}", fields.DiscNumber); AaruConsole.DebugWriteLine("Nintendo plugin", "discVersion = {0}", fields.DiscVersion); AaruConsole.DebugWriteLine("Nintendo plugin", "streaming = {0}", fields.Streaming); AaruConsole.DebugWriteLine("Nintendo plugin", "streamBufferSize = {0}", fields.StreamBufferSize); AaruConsole.DebugWriteLine("Nintendo plugin", "title = \"{0}\"", fields.Title); AaruConsole.DebugWriteLine("Nintendo plugin", "debugOff = 0x{0:X8}", fields.DebugOff); AaruConsole.DebugWriteLine("Nintendo plugin", "debugAddr = 0x{0:X8}", fields.DebugAddr); AaruConsole.DebugWriteLine("Nintendo plugin", "dolOff = 0x{0:X8}", fields.DolOff); AaruConsole.DebugWriteLine("Nintendo plugin", "fstOff = 0x{0:X8}", fields.FstOff); AaruConsole.DebugWriteLine("Nintendo plugin", "fstSize = {0}", fields.FstSize); AaruConsole.DebugWriteLine("Nintendo plugin", "fstMax = {0}", fields.FstMax); for (int i = 0; i < fields.FirstPartitions.Length; i++) { AaruConsole.DebugWriteLine("Nintendo plugin", "firstPartitions[{1}].offset = {0}", fields.FirstPartitions[i].Offset, i); AaruConsole.DebugWriteLine("Nintendo plugin", "firstPartitions[{1}].type = {0}", fields.FirstPartitions[i].Type, i); } for (int i = 0; i < fields.SecondPartitions.Length; i++) { AaruConsole.DebugWriteLine("Nintendo plugin", "secondPartitions[{1}].offset = {0}", fields.SecondPartitions[i].Offset, i); AaruConsole.DebugWriteLine("Nintendo plugin", "secondPartitions[{1}].type = {0}", fields.SecondPartitions[i].Type, i); } for (int i = 0; i < fields.ThirdPartitions.Length; i++) { AaruConsole.DebugWriteLine("Nintendo plugin", "thirdPartitions[{1}].offset = {0}", fields.ThirdPartitions[i].Offset, i); AaruConsole.DebugWriteLine("Nintendo plugin", "thirdPartitions[{1}].type = {0}", fields.ThirdPartitions[i].Type, i); } for (int i = 0; i < fields.FourthPartitions.Length; i++) { AaruConsole.DebugWriteLine("Nintendo plugin", "fourthPartitions[{1}].offset = {0}", fields.FourthPartitions[i].Offset, i); AaruConsole.DebugWriteLine("Nintendo plugin", "fourthPartitions[{1}].type = {0}", fields.FourthPartitions[i].Type, i); } AaruConsole.DebugWriteLine("Nintendo plugin", "region = {0}", fields.Region); AaruConsole.DebugWriteLine("Nintendo plugin", "japanAge = {0}", fields.JapanAge); AaruConsole.DebugWriteLine("Nintendo plugin", "usaAge = {0}", fields.UsaAge); AaruConsole.DebugWriteLine("Nintendo plugin", "germanAge = {0}", fields.GermanAge); AaruConsole.DebugWriteLine("Nintendo plugin", "pegiAge = {0}", fields.PegiAge); AaruConsole.DebugWriteLine("Nintendo plugin", "finlandAge = {0}", fields.FinlandAge); AaruConsole.DebugWriteLine("Nintendo plugin", "portugalAge = {0}", fields.PortugalAge); AaruConsole.DebugWriteLine("Nintendo plugin", "ukAge = {0}", fields.UkAge); AaruConsole.DebugWriteLine("Nintendo plugin", "australiaAge = {0}", fields.AustraliaAge); AaruConsole.DebugWriteLine("Nintendo plugin", "koreaAge = {0}", fields.KoreaAge); sbInformation.AppendLine("Nintendo optical filesystem"); sbInformation.AppendLine(wii ? "Nintendo Wii Optical Disc" : "Nintendo GameCube Optical Disc"); sbInformation.AppendFormat("Disc ID is {0}", fields.DiscId).AppendLine(); sbInformation.AppendFormat("Disc is a {0} disc", DiscTypeToString(fields.DiscType)).AppendLine(); sbInformation.AppendFormat("Disc region is {0}", RegionCodeToString(fields.RegionCode)).AppendLine(); sbInformation.AppendFormat("Published by {0}", PublisherCodeToString(fields.PublisherCode)).AppendLine(); if (fields.DiscNumber > 0) { sbInformation.AppendFormat("Disc number {0} of a multi-disc set", fields.DiscNumber + 1).AppendLine(); } if (fields.Streaming) { sbInformation.AppendLine("Disc is prepared for audio streaming"); } if (fields.StreamBufferSize > 0) { sbInformation.AppendFormat("Audio streaming buffer size is {0} bytes", fields.StreamBufferSize). AppendLine(); } sbInformation.AppendFormat("Title: {0}", fields.Title).AppendLine(); if (wii) { for (int i = 0; i < fields.FirstPartitions.Length; i++) { sbInformation.AppendFormat("First {0} partition starts at sector {1}", PartitionTypeToString(fields.FirstPartitions[i].Type), fields.FirstPartitions[i].Offset / 2048).AppendLine(); } for (int i = 0; i < fields.SecondPartitions.Length; i++) { sbInformation.AppendFormat("Second {0} partition starts at sector {1}", PartitionTypeToString(fields.SecondPartitions[i].Type), fields.SecondPartitions[i].Offset / 2048).AppendLine(); } for (int i = 0; i < fields.ThirdPartitions.Length; i++) { sbInformation.AppendFormat("Third {0} partition starts at sector {1}", PartitionTypeToString(fields.ThirdPartitions[i].Type), fields.ThirdPartitions[i].Offset / 2048).AppendLine(); } for (int i = 0; i < fields.FourthPartitions.Length; i++) { sbInformation.AppendFormat("Fourth {0} partition starts at sector {1}", PartitionTypeToString(fields.FourthPartitions[i].Type), fields.FourthPartitions[i].Offset / 2048).AppendLine(); } // sbInformation.AppendFormat("Region byte is {0}", fields.region).AppendLine(); if ((fields.JapanAge & 0x80) != 0x80) { sbInformation.AppendFormat("Japan age rating is {0}", fields.JapanAge).AppendLine(); } if ((fields.UsaAge & 0x80) != 0x80) { sbInformation.AppendFormat("ESRB age rating is {0}", fields.UsaAge).AppendLine(); } if ((fields.GermanAge & 0x80) != 0x80) { sbInformation.AppendFormat("German age rating is {0}", fields.GermanAge).AppendLine(); } if ((fields.PegiAge & 0x80) != 0x80) { sbInformation.AppendFormat("PEGI age rating is {0}", fields.PegiAge).AppendLine(); } if ((fields.FinlandAge & 0x80) != 0x80) { sbInformation.AppendFormat("Finland age rating is {0}", fields.FinlandAge).AppendLine(); } if ((fields.PortugalAge & 0x80) != 0x80) { sbInformation.AppendFormat("Portugal age rating is {0}", fields.PortugalAge).AppendLine(); } if ((fields.UkAge & 0x80) != 0x80) { sbInformation.AppendFormat("UK age rating is {0}", fields.UkAge).AppendLine(); } if ((fields.AustraliaAge & 0x80) != 0x80) { sbInformation.AppendFormat("Australia age rating is {0}", fields.AustraliaAge).AppendLine(); } if ((fields.KoreaAge & 0x80) != 0x80) { sbInformation.AppendFormat("Korea age rating is {0}", fields.KoreaAge).AppendLine(); } } else { sbInformation.AppendFormat("FST starts at {0} and has {1} bytes", fields.FstOff, fields.FstSize). AppendLine(); } information = sbInformation.ToString(); XmlFsType.Bootable = true; XmlFsType.Clusters = (imagePlugin.Info.Sectors * imagePlugin.Info.SectorSize) / 2048; XmlFsType.ClusterSize = 2048; XmlFsType.Type = wii ? "Nintendo Wii filesystem" : "Nintendo Gamecube filesystem"; XmlFsType.VolumeName = fields.Title; XmlFsType.VolumeSerial = fields.DiscId; }
public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, Encoding encoding) { Encoding = encoding ?? Encoding.GetEncoding("iso-8859-15"); information = ""; if (imagePlugin.Info.SectorSize < 512) { return; } uint sbAddr = REISER4_SUPER_OFFSET / imagePlugin.Info.SectorSize; if (sbAddr == 0) { sbAddr = 1; } Reiser4_Superblock reiserSb = new Reiser4_Superblock(); uint sbSize = (uint)(Marshal.SizeOf(reiserSb) / imagePlugin.Info.SectorSize); if (Marshal.SizeOf(reiserSb) % imagePlugin.Info.SectorSize != 0) { sbSize++; } byte[] sector = imagePlugin.ReadSectors(partition.Start + sbAddr, sbSize); if (sector.Length < Marshal.SizeOf(reiserSb)) { return; } IntPtr sbPtr = Marshal.AllocHGlobal(Marshal.SizeOf(reiserSb)); Marshal.Copy(sector, 0, sbPtr, Marshal.SizeOf(reiserSb)); reiserSb = (Reiser4_Superblock)Marshal.PtrToStructure(sbPtr, typeof(Reiser4_Superblock)); Marshal.FreeHGlobal(sbPtr); if (!reiser4_magic.SequenceEqual(reiserSb.magic)) { return; } StringBuilder sb = new StringBuilder(); sb.AppendLine("Reiser 4 filesystem"); sb.AppendFormat("{0} bytes per block", reiserSb.blocksize).AppendLine(); sb.AppendFormat("Volume disk format: {0}", reiserSb.diskformat).AppendLine(); sb.AppendFormat("Volume UUID: {0}", reiserSb.uuid).AppendLine(); sb.AppendFormat("Volume name: {0}", StringHandlers.CToString(reiserSb.label, Encoding)).AppendLine(); information = sb.ToString(); XmlFsType = new FileSystemType { Type = "Reiser 4 filesystem", ClusterSize = reiserSb.blocksize, Clusters = (long)((partition.End - partition.Start) * imagePlugin.Info.SectorSize / reiserSb.blocksize), VolumeName = StringHandlers.CToString(reiserSb.label, Encoding), VolumeSerial = reiserSb.uuid.ToString() }; }
static BpbKind DetectBpbKind(byte[] bpbSector, IMediaImage imagePlugin, Partition partition, out BiosParameterBlockEbpb fakeBpb, out HumanParameterBlock humanBpb, out AtariParameterBlock atariBpb, out byte minBootNearJump, out bool andosOemCorrect, out bool bootable) { fakeBpb = new BiosParameterBlockEbpb(); minBootNearJump = 0; andosOemCorrect = false; bootable = false; humanBpb = Marshal.ByteArrayToStructureBigEndian <HumanParameterBlock>(bpbSector); atariBpb = Marshal.ByteArrayToStructureLittleEndian <AtariParameterBlock>(bpbSector); ulong expectedClusters = humanBpb.bpc > 0 ? partition.Size / humanBpb.bpc : 0; // Check clusters for Human68k are correct bool humanClustersCorrect = humanBpb.clusters == 0 ? humanBpb.big_clusters == expectedClusters : humanBpb.clusters == expectedClusters; // Check OEM for Human68k is correct bool humanOemCorrect = bpbSector[2] >= 0x20 && bpbSector[3] >= 0x20 && bpbSector[4] >= 0x20 && bpbSector[5] >= 0x20 && bpbSector[6] >= 0x20 && bpbSector[7] >= 0x20 && bpbSector[8] >= 0x20 && bpbSector[9] >= 0x20 && bpbSector[10] >= 0x20 && bpbSector[11] >= 0x20 && bpbSector[12] >= 0x20 && bpbSector[13] >= 0x20 && bpbSector[14] >= 0x20 && bpbSector[15] >= 0x20 && bpbSector[16] >= 0x20 && bpbSector[17] >= 0x20; // Check correct branch for Human68k bool humanBranchCorrect = bpbSector[0] == 0x60 && bpbSector[1] >= 0x1C && bpbSector[1] < 0xFE; DicConsole.DebugWriteLine("FAT plugin", "humanClustersCorrect = {0}", humanClustersCorrect); DicConsole.DebugWriteLine("FAT plugin", "humanOemCorrect = {0}", humanOemCorrect); DicConsole.DebugWriteLine("FAT plugin", "humanBranchCorrect = {0}", humanBranchCorrect); // If all Human68k checks are correct, it is a Human68k FAT16 bool useHumanBpb = humanClustersCorrect && humanOemCorrect && humanBranchCorrect && expectedClusters > 0; if (useHumanBpb) { DicConsole.DebugWriteLine("FAT plugin", "Using Human68k BPB"); fakeBpb.jump = humanBpb.jump; fakeBpb.oem_name = humanBpb.oem_name; fakeBpb.bps = (ushort)imagePlugin.Info.SectorSize; fakeBpb.spc = (byte)(humanBpb.bpc / fakeBpb.bps); fakeBpb.fats_no = 2; fakeBpb.root_ent = humanBpb.root_ent; fakeBpb.media = humanBpb.media; fakeBpb.spfat = (ushort)(humanBpb.cpfat * fakeBpb.spc); fakeBpb.boot_code = humanBpb.boot_code; fakeBpb.sectors = humanBpb.clusters; fakeBpb.big_sectors = humanBpb.big_clusters; fakeBpb.rsectors = 1; return(BpbKind.Human); } MsxParameterBlock msxBpb = new MsxParameterBlock(); BiosParameterBlock2 dos2Bpb = new BiosParameterBlock2(); BiosParameterBlock30 dos30Bpb = new BiosParameterBlock30(); BiosParameterBlock32 dos32Bpb = new BiosParameterBlock32(); BiosParameterBlock33 dos33Bpb = new BiosParameterBlock33(); BiosParameterBlockShortEbpb shortEbpb = new BiosParameterBlockShortEbpb(); BiosParameterBlockEbpb ebpb = new BiosParameterBlockEbpb(); Fat32ParameterBlockShort shortFat32Bpb = new Fat32ParameterBlockShort(); Fat32ParameterBlock fat32Bpb = new Fat32ParameterBlock(); ApricotLabel apricotBpb = new ApricotLabel(); bool useAtariBpb = false; bool useMsxBpb = false; bool useDos2Bpb = false; bool useDos3Bpb = false; bool useDos32Bpb = false; bool useDos33Bpb = false; bool userShortExtendedBpb = false; bool useExtendedBpb = false; bool useShortFat32 = false; bool useLongFat32 = false; bool useApricotBpb = false; bool useDecRainbowBpb = false; if (imagePlugin.Info.SectorSize >= 256 && !useHumanBpb) { msxBpb = Marshal.ByteArrayToStructureLittleEndian <MsxParameterBlock>(bpbSector); dos2Bpb = Marshal.ByteArrayToStructureLittleEndian <BiosParameterBlock2>(bpbSector); dos30Bpb = Marshal.ByteArrayToStructureLittleEndian <BiosParameterBlock30>(bpbSector); dos32Bpb = Marshal.ByteArrayToStructureLittleEndian <BiosParameterBlock32>(bpbSector); dos33Bpb = Marshal.ByteArrayToStructureLittleEndian <BiosParameterBlock33>(bpbSector); shortEbpb = Marshal.ByteArrayToStructureLittleEndian <BiosParameterBlockShortEbpb>(bpbSector); ebpb = Marshal.ByteArrayToStructureLittleEndian <BiosParameterBlockEbpb>(bpbSector); shortFat32Bpb = Marshal.ByteArrayToStructureLittleEndian <Fat32ParameterBlockShort>(bpbSector); fat32Bpb = Marshal.ByteArrayToStructureLittleEndian <Fat32ParameterBlock>(bpbSector); apricotBpb = Marshal.ByteArrayToStructureLittleEndian <ApricotLabel>(bpbSector); int bitsInBpsMsx = CountBits.Count(msxBpb.bps); int bitsInBpsDos33 = CountBits.Count(dos33Bpb.bps); int bitsInBpsDos40 = CountBits.Count(ebpb.bps); int bitsInBpsFat32Short = CountBits.Count(shortFat32Bpb.bps); int bitsInBpsFat32 = CountBits.Count(fat32Bpb.bps); int bitsInBpsApricot = CountBits.Count(apricotBpb.mainBPB.bps); bool correctSpcMsx = msxBpb.spc == 1 || msxBpb.spc == 2 || msxBpb.spc == 4 || msxBpb.spc == 8 || msxBpb.spc == 16 || msxBpb.spc == 32 || msxBpb.spc == 64; bool correctSpcDos33 = dos33Bpb.spc == 1 || dos33Bpb.spc == 2 || dos33Bpb.spc == 4 || dos33Bpb.spc == 8 || dos33Bpb.spc == 16 || dos33Bpb.spc == 32 || dos33Bpb.spc == 64; bool correctSpcDos40 = ebpb.spc == 1 || ebpb.spc == 2 || ebpb.spc == 4 || ebpb.spc == 8 || ebpb.spc == 16 || ebpb.spc == 32 || ebpb.spc == 64; bool correctSpcFat32Short = shortFat32Bpb.spc == 1 || shortFat32Bpb.spc == 2 || shortFat32Bpb.spc == 4 || shortFat32Bpb.spc == 8 || shortFat32Bpb.spc == 16 || shortFat32Bpb.spc == 32 || shortFat32Bpb.spc == 64; bool correctSpcFat32 = fat32Bpb.spc == 1 || fat32Bpb.spc == 2 || fat32Bpb.spc == 4 || fat32Bpb.spc == 8 || fat32Bpb.spc == 16 || fat32Bpb.spc == 32 || fat32Bpb.spc == 64; bool correctSpcApricot = apricotBpb.mainBPB.spc == 1 || apricotBpb.mainBPB.spc == 2 || apricotBpb.mainBPB.spc == 4 || apricotBpb.mainBPB.spc == 8 || apricotBpb.mainBPB.spc == 16 || apricotBpb.mainBPB.spc == 32 || apricotBpb.mainBPB.spc == 64; // This is to support FAT partitions on hybrid ISO/USB images if (imagePlugin.Info.XmlMediaType == XmlMediaType.OpticalDisc) { atariBpb.sectors /= 4; msxBpb.sectors /= 4; dos2Bpb.sectors /= 4; dos30Bpb.sectors /= 4; dos32Bpb.sectors /= 4; dos33Bpb.sectors /= 4; dos33Bpb.big_sectors /= 4; shortEbpb.sectors /= 4; shortEbpb.big_sectors /= 4; ebpb.sectors /= 4; ebpb.big_sectors /= 4; shortFat32Bpb.sectors /= 4; shortFat32Bpb.big_sectors /= 4; shortFat32Bpb.huge_sectors /= 4; fat32Bpb.sectors /= 4; fat32Bpb.big_sectors /= 4; apricotBpb.mainBPB.sectors /= 4; } andosOemCorrect = dos33Bpb.oem_name[0] < 0x20 && dos33Bpb.oem_name[1] >= 0x20 && dos33Bpb.oem_name[2] >= 0x20 && dos33Bpb.oem_name[3] >= 0x20 && dos33Bpb.oem_name[4] >= 0x20 && dos33Bpb.oem_name[5] >= 0x20 && dos33Bpb.oem_name[6] >= 0x20 && dos33Bpb.oem_name[7] >= 0x20; if (bitsInBpsFat32 == 1 && correctSpcFat32 && fat32Bpb.fats_no <= 2 && fat32Bpb.sectors == 0 && fat32Bpb.spfat == 0 && fat32Bpb.signature == 0x29 && Encoding.ASCII.GetString(fat32Bpb.fs_type) == "FAT32 ") { DicConsole.DebugWriteLine("FAT plugin", "Using FAT32 BPB"); useLongFat32 = true; minBootNearJump = 0x58; return(BpbKind.LongFat32); } if (bitsInBpsFat32Short == 1 && correctSpcFat32Short && shortFat32Bpb.fats_no <= 2 && shortFat32Bpb.sectors == 0 && shortFat32Bpb.spfat == 0 && shortFat32Bpb.signature == 0x28) { DicConsole.DebugWriteLine("FAT plugin", "Using short FAT32 BPB"); useShortFat32 = shortFat32Bpb.big_sectors == 0 ? shortFat32Bpb.huge_sectors <= partition.End - partition.Start + 1 : shortFat32Bpb.big_sectors <= partition.End - partition.Start + 1; minBootNearJump = 0x57; return(BpbKind.ShortFat32); } if (bitsInBpsMsx == 1 && correctSpcMsx && msxBpb.fats_no <= 2 && msxBpb.root_ent > 0 && msxBpb.sectors <= partition.End - partition.Start + 1 && msxBpb.spfat > 0 && Encoding.ASCII.GetString(msxBpb.vol_id) == "VOL_ID") { DicConsole.DebugWriteLine("FAT plugin", "Using MSX BPB"); useMsxBpb = true; } else if (bitsInBpsApricot == 1 && correctSpcApricot && apricotBpb.mainBPB.fats_no <= 2 && apricotBpb.mainBPB.root_ent > 0 && apricotBpb.mainBPB.sectors <= partition.End - partition.Start + 1 && apricotBpb.mainBPB.spfat > 0 && apricotBpb.partitionCount == 0) { DicConsole.DebugWriteLine("FAT plugin", "Using Apricot BPB"); useApricotBpb = true; } else if (bitsInBpsDos40 == 1 && correctSpcDos40 && ebpb.fats_no <= 2 && ebpb.root_ent > 0 && ebpb.spfat > 0 && (ebpb.signature == 0x28 || ebpb.signature == 0x29 || andosOemCorrect)) { if (ebpb.sectors == 0) { if (ebpb.big_sectors <= partition.End - partition.Start + 1) { if (ebpb.signature == 0x29 || andosOemCorrect) { DicConsole.DebugWriteLine("FAT plugin", "Using DOS 4.0 BPB"); useExtendedBpb = true; minBootNearJump = 0x3C; } else { DicConsole.DebugWriteLine("FAT plugin", "Using DOS 3.4 BPB"); userShortExtendedBpb = true; minBootNearJump = 0x29; } } } else if (ebpb.sectors <= partition.End - partition.Start + 1) { if (ebpb.signature == 0x29 || andosOemCorrect) { DicConsole.DebugWriteLine("FAT plugin", "Using DOS 4.0 BPB"); useExtendedBpb = true; minBootNearJump = 0x3C; } else { DicConsole.DebugWriteLine("FAT plugin", "Using DOS 3.4 BPB"); userShortExtendedBpb = true; minBootNearJump = 0x29; } } } else if (bitsInBpsDos33 == 1 && correctSpcDos33 && dos33Bpb.rsectors < partition.End - partition.Start && dos33Bpb.fats_no <= 2 && dos33Bpb.root_ent > 0 && dos33Bpb.spfat > 0) { if (dos33Bpb.sectors == 0 && dos33Bpb.hsectors <= partition.Start && dos33Bpb.big_sectors > 0 && dos33Bpb.big_sectors <= partition.End - partition.Start + 1) { DicConsole.DebugWriteLine("FAT plugin", "Using DOS 3.3 BPB"); useDos33Bpb = true; minBootNearJump = 0x22; } else if (dos33Bpb.big_sectors == 0 && dos33Bpb.hsectors <= partition.Start && dos33Bpb.sectors > 0 && dos33Bpb.sectors <= partition.End - partition.Start + 1) { if (atariBpb.jump[0] == 0x60 || atariBpb.jump[0] == 0xE9 && atariBpb.jump[1] == 0x00 && Encoding.ASCII.GetString(dos33Bpb.oem_name) != "NEXT ") { DicConsole.DebugWriteLine("FAT plugin", "Using Atari BPB"); useAtariBpb = true; } else { DicConsole.DebugWriteLine("FAT plugin", "Using DOS 3.3 BPB"); useDos33Bpb = true; minBootNearJump = 0x22; } } else { if (dos32Bpb.hsectors <= partition.Start && dos32Bpb.hsectors + dos32Bpb.sectors == dos32Bpb.total_sectors) { DicConsole.DebugWriteLine("FAT plugin", "Using DOS 3.2 BPB"); useDos32Bpb = true; minBootNearJump = 0x1E; } else if (dos30Bpb.sptrk > 0 && dos30Bpb.sptrk < 64 && dos30Bpb.heads > 0 && dos30Bpb.heads < 256) { if (atariBpb.jump[0] == 0x60 || atariBpb.jump[0] == 0xE9 && atariBpb.jump[1] == 0x00 && Encoding.ASCII.GetString(dos33Bpb.oem_name) != "NEXT ") { DicConsole.DebugWriteLine("FAT plugin", "Using Atari BPB"); useAtariBpb = true; } else { DicConsole.DebugWriteLine("FAT plugin", "Using DOS 3.0 BPB"); useDos3Bpb = true; minBootNearJump = 0x1C; } } else { if (atariBpb.jump[0] == 0x60 || atariBpb.jump[0] == 0xE9 && atariBpb.jump[1] == 0x00 && Encoding.ASCII.GetString(dos33Bpb.oem_name) != "NEXT ") { DicConsole.DebugWriteLine("FAT plugin", "Using Atari BPB"); useAtariBpb = true; } else { DicConsole.DebugWriteLine("FAT plugin", "Using DOS 2.0 BPB"); useDos2Bpb = true; minBootNearJump = 0x16; } } } } } // DEC Rainbow, lacks a BPB but has a very concrete structure... if (imagePlugin.Info.Sectors == 800 && imagePlugin.Info.SectorSize == 512 && !useAtariBpb && !useMsxBpb && !useDos2Bpb && !useDos3Bpb && !useDos32Bpb && !useDos33Bpb && !userShortExtendedBpb && !useExtendedBpb && !useHumanBpb && !useShortFat32 && !useLongFat32 && !useApricotBpb) { // DEC Rainbow boots up with a Z80, first byte should be DI (disable interrupts) byte z80Di = bpbSector[0]; // First FAT1 sector resides at LBA 0x14 byte[] fat1Sector0 = imagePlugin.ReadSector(0x14); // First FAT2 sector resides at LBA 0x1A byte[] fat2Sector0 = imagePlugin.ReadSector(0x1A); bool equalFatIds = fat1Sector0[0] == fat2Sector0[0] && fat1Sector0[1] == fat2Sector0[1]; // Volume is software interleaved 2:1 MemoryStream rootMs = new MemoryStream(); foreach (byte[] tmp in from ulong rootSector in new[] { 0x17, 0x19, 0x1B, 0x1D, 0x1E, 0x20 } select imagePlugin.ReadSector(rootSector)) { rootMs.Write(tmp, 0, tmp.Length); } byte[] rootDir = rootMs.ToArray(); bool validRootDir = true; // Iterate all root directory for (int e = 0; e < 96 * 32; e += 32) { for (int c = 0; c < 11; c++) { if (rootDir[c + e] < 0x20 && rootDir[c + e] != 0x00 && rootDir[c + e] != 0x05 || rootDir[c + e] == 0xFF || rootDir[c + e] == 0x2E) { validRootDir = false; break; } } if (!validRootDir) { break; } } if (z80Di == 0xF3 && equalFatIds && (fat1Sector0[0] & 0xF0) == 0xF0 && fat1Sector0[1] == 0xFF && validRootDir) { useDecRainbowBpb = true; DicConsole.DebugWriteLine("FAT plugin", "Using DEC Rainbow hardcoded BPB."); fakeBpb.bps = 512; fakeBpb.spc = 1; fakeBpb.rsectors = 20; fakeBpb.fats_no = 2; fakeBpb.root_ent = 96; fakeBpb.sectors = 800; fakeBpb.media = 0xFA; fakeBpb.sptrk = 10; fakeBpb.heads = 1; fakeBpb.hsectors = 0; fakeBpb.spfat = 3; bootable = true; fakeBpb.boot_code = bpbSector; return(BpbKind.DecRainbow); } } if (!useAtariBpb && !useMsxBpb && !useDos2Bpb && !useDos3Bpb && !useDos32Bpb && !useDos33Bpb && !useHumanBpb && !userShortExtendedBpb && !useExtendedBpb && !useShortFat32 && !useLongFat32 && !useApricotBpb && !useDecRainbowBpb) { byte[] fatSector = imagePlugin.ReadSector(1 + partition.Start); switch (fatSector[0]) { case 0xE5: if (imagePlugin.Info.Sectors == 2002 && imagePlugin.Info.SectorSize == 128) { DicConsole.DebugWriteLine("FAT plugin", "Using hardcoded BPB."); fakeBpb.bps = 128; fakeBpb.spc = 4; fakeBpb.rsectors = 1; fakeBpb.fats_no = 2; fakeBpb.root_ent = 64; fakeBpb.sectors = 2002; fakeBpb.media = 0xE5; fakeBpb.sptrk = 26; fakeBpb.heads = 1; fakeBpb.hsectors = 0; fakeBpb.spfat = 1; } break; case 0xFD: if (imagePlugin.Info.Sectors == 4004 && imagePlugin.Info.SectorSize == 128) { DicConsole.DebugWriteLine("FAT plugin", "Using hardcoded BPB."); fakeBpb.bps = 128; fakeBpb.spc = 4; fakeBpb.rsectors = 4; fakeBpb.fats_no = 2; fakeBpb.root_ent = 68; fakeBpb.sectors = 4004; fakeBpb.media = 0xFD; fakeBpb.sptrk = 26; fakeBpb.heads = 2; fakeBpb.hsectors = 0; fakeBpb.spfat = 6; } else if (imagePlugin.Info.Sectors == 2002 && imagePlugin.Info.SectorSize == 128) { DicConsole.DebugWriteLine("FAT plugin", "Using hardcoded BPB."); fakeBpb.bps = 128; fakeBpb.spc = 4; fakeBpb.rsectors = 4; fakeBpb.fats_no = 2; fakeBpb.root_ent = 68; fakeBpb.sectors = 2002; fakeBpb.media = 0xFD; fakeBpb.sptrk = 26; fakeBpb.heads = 1; fakeBpb.hsectors = 0; fakeBpb.spfat = 6; } break; case 0xFE: if (imagePlugin.Info.Sectors == 320 && imagePlugin.Info.SectorSize == 512) { DicConsole.DebugWriteLine("FAT plugin", "Using hardcoded BPB for 5.25\" SSDD."); fakeBpb.bps = 512; fakeBpb.spc = 1; fakeBpb.rsectors = 1; fakeBpb.fats_no = 2; fakeBpb.root_ent = 64; fakeBpb.sectors = 320; fakeBpb.media = 0xFE; fakeBpb.sptrk = 8; fakeBpb.heads = 1; fakeBpb.hsectors = 0; fakeBpb.spfat = 1; } else if (imagePlugin.Info.Sectors == 2002 && imagePlugin.Info.SectorSize == 128) { DicConsole.DebugWriteLine("FAT plugin", "Using hardcoded BPB."); fakeBpb.bps = 128; fakeBpb.spc = 4; fakeBpb.rsectors = 1; fakeBpb.fats_no = 2; fakeBpb.root_ent = 68; fakeBpb.sectors = 2002; fakeBpb.media = 0xFE; fakeBpb.sptrk = 26; fakeBpb.heads = 1; fakeBpb.hsectors = 0; fakeBpb.spfat = 6; } else if (imagePlugin.Info.Sectors == 1232 && imagePlugin.Info.SectorSize == 1024) { DicConsole.DebugWriteLine("FAT plugin", "Using hardcoded BPB."); fakeBpb.bps = 1024; fakeBpb.spc = 1; fakeBpb.rsectors = 1; fakeBpb.fats_no = 2; fakeBpb.root_ent = 192; fakeBpb.sectors = 1232; fakeBpb.media = 0xFE; fakeBpb.sptrk = 8; fakeBpb.heads = 2; fakeBpb.hsectors = 0; fakeBpb.spfat = 2; } else if (imagePlugin.Info.Sectors == 616 && imagePlugin.Info.SectorSize == 1024) { DicConsole.DebugWriteLine("FAT plugin", "Using hardcoded BPB."); fakeBpb.bps = 1024; fakeBpb.spc = 1; fakeBpb.rsectors = 1; fakeBpb.fats_no = 2; fakeBpb.root_ent = 6192; fakeBpb.sectors = 616; fakeBpb.media = 0xFE; fakeBpb.sptrk = 8; fakeBpb.heads = 2; fakeBpb.hsectors = 0; } else if (imagePlugin.Info.Sectors == 720 && imagePlugin.Info.SectorSize == 128) { DicConsole.DebugWriteLine("FAT plugin", "Using hardcoded BPB."); fakeBpb.bps = 128; fakeBpb.spc = 2; fakeBpb.rsectors = 54; fakeBpb.fats_no = 2; fakeBpb.root_ent = 64; fakeBpb.sectors = 720; fakeBpb.media = 0xFE; fakeBpb.sptrk = 18; fakeBpb.heads = 1; fakeBpb.hsectors = 0; fakeBpb.spfat = 4; } else if (imagePlugin.Info.Sectors == 640 && imagePlugin.Info.SectorSize == 512) { DicConsole.DebugWriteLine("FAT plugin", "Using hardcoded BPB for 5.25\" DSDD."); fakeBpb.bps = 512; fakeBpb.spc = 2; fakeBpb.rsectors = 1; fakeBpb.fats_no = 2; fakeBpb.root_ent = 112; fakeBpb.sectors = 640; fakeBpb.media = 0xFF; fakeBpb.sptrk = 8; fakeBpb.heads = 2; fakeBpb.hsectors = 0; fakeBpb.spfat = 1; } break; case 0xFF: if (imagePlugin.Info.Sectors == 640 && imagePlugin.Info.SectorSize == 512) { DicConsole.DebugWriteLine("FAT plugin", "Using hardcoded BPB for 5.25\" DSDD."); fakeBpb.bps = 512; fakeBpb.spc = 2; fakeBpb.rsectors = 1; fakeBpb.fats_no = 2; fakeBpb.root_ent = 112; fakeBpb.sectors = 640; fakeBpb.media = 0xFF; fakeBpb.sptrk = 8; fakeBpb.heads = 2; fakeBpb.hsectors = 0; fakeBpb.spfat = 1; } break; } // This assumes a bootable sector will jump somewhere or disable interrupts in x86 code bootable |= bpbSector[0] == 0xFA || bpbSector[0] == 0xEB && bpbSector[1] <= 0x7F || bpbSector[0] == 0xE9 && BitConverter.ToUInt16(bpbSector, 1) <= 0x1FC; fakeBpb.boot_code = bpbSector; return(BpbKind.Hardcoded); } if (useExtendedBpb) { fakeBpb = ebpb; return(BpbKind.Extended); } if (userShortExtendedBpb) { fakeBpb.jump = shortEbpb.jump; fakeBpb.oem_name = shortEbpb.oem_name; fakeBpb.bps = shortEbpb.bps; fakeBpb.spc = shortEbpb.spc; fakeBpb.rsectors = shortEbpb.rsectors; fakeBpb.fats_no = shortEbpb.fats_no; fakeBpb.root_ent = shortEbpb.root_ent; fakeBpb.sectors = shortEbpb.sectors; fakeBpb.media = shortEbpb.media; fakeBpb.spfat = shortEbpb.spfat; fakeBpb.sptrk = shortEbpb.sptrk; fakeBpb.heads = shortEbpb.heads; fakeBpb.hsectors = shortEbpb.hsectors; fakeBpb.big_sectors = shortEbpb.big_sectors; fakeBpb.drive_no = shortEbpb.drive_no; fakeBpb.flags = shortEbpb.flags; fakeBpb.signature = shortEbpb.signature; fakeBpb.serial_no = shortEbpb.serial_no; fakeBpb.boot_code = shortEbpb.boot_code; fakeBpb.boot_signature = shortEbpb.boot_signature; return(BpbKind.ShortExtended); } if (useDos33Bpb) { fakeBpb.jump = dos33Bpb.jump; fakeBpb.oem_name = dos33Bpb.oem_name; fakeBpb.bps = dos33Bpb.bps; fakeBpb.spc = dos33Bpb.spc; fakeBpb.rsectors = dos33Bpb.rsectors; fakeBpb.fats_no = dos33Bpb.fats_no; fakeBpb.root_ent = dos33Bpb.root_ent; fakeBpb.sectors = dos33Bpb.sectors; fakeBpb.media = dos33Bpb.media; fakeBpb.spfat = dos33Bpb.spfat; fakeBpb.sptrk = dos33Bpb.sptrk; fakeBpb.heads = dos33Bpb.heads; fakeBpb.hsectors = dos33Bpb.hsectors; fakeBpb.big_sectors = dos33Bpb.big_sectors; fakeBpb.boot_code = dos33Bpb.boot_code; fakeBpb.boot_signature = dos33Bpb.boot_signature; return(BpbKind.Dos33); } if (useDos32Bpb) { fakeBpb.jump = dos32Bpb.jump; fakeBpb.oem_name = dos32Bpb.oem_name; fakeBpb.bps = dos32Bpb.bps; fakeBpb.spc = dos32Bpb.spc; fakeBpb.rsectors = dos32Bpb.rsectors; fakeBpb.fats_no = dos32Bpb.fats_no; fakeBpb.root_ent = dos32Bpb.root_ent; fakeBpb.sectors = dos32Bpb.sectors; fakeBpb.media = dos32Bpb.media; fakeBpb.spfat = dos32Bpb.spfat; fakeBpb.sptrk = dos32Bpb.sptrk; fakeBpb.heads = dos32Bpb.heads; fakeBpb.hsectors = dos32Bpb.hsectors; fakeBpb.boot_code = dos32Bpb.boot_code; fakeBpb.boot_signature = dos32Bpb.boot_signature; return(BpbKind.Dos32); } if (useDos3Bpb) { fakeBpb.jump = dos30Bpb.jump; fakeBpb.oem_name = dos30Bpb.oem_name; fakeBpb.bps = dos30Bpb.bps; fakeBpb.spc = dos30Bpb.spc; fakeBpb.rsectors = dos30Bpb.rsectors; fakeBpb.fats_no = dos30Bpb.fats_no; fakeBpb.root_ent = dos30Bpb.root_ent; fakeBpb.sectors = dos30Bpb.sectors; fakeBpb.media = dos30Bpb.media; fakeBpb.spfat = dos30Bpb.spfat; fakeBpb.sptrk = dos30Bpb.sptrk; fakeBpb.heads = dos30Bpb.heads; fakeBpb.hsectors = dos30Bpb.hsectors; fakeBpb.boot_code = dos30Bpb.boot_code; fakeBpb.boot_signature = dos30Bpb.boot_signature; return(BpbKind.Dos3); } if (useDos2Bpb) { fakeBpb.jump = dos2Bpb.jump; fakeBpb.oem_name = dos2Bpb.oem_name; fakeBpb.bps = dos2Bpb.bps; fakeBpb.spc = dos2Bpb.spc; fakeBpb.rsectors = dos2Bpb.rsectors; fakeBpb.fats_no = dos2Bpb.fats_no; fakeBpb.root_ent = dos2Bpb.root_ent; fakeBpb.sectors = dos2Bpb.sectors; fakeBpb.media = dos2Bpb.media; fakeBpb.spfat = dos2Bpb.spfat; fakeBpb.boot_code = dos2Bpb.boot_code; fakeBpb.boot_signature = dos2Bpb.boot_signature; return(BpbKind.Dos2); } if (useMsxBpb) { fakeBpb.jump = msxBpb.jump; fakeBpb.oem_name = msxBpb.oem_name; fakeBpb.bps = msxBpb.bps; fakeBpb.spc = msxBpb.spc; fakeBpb.rsectors = msxBpb.rsectors; fakeBpb.fats_no = msxBpb.fats_no; fakeBpb.root_ent = msxBpb.root_ent; fakeBpb.sectors = msxBpb.sectors; fakeBpb.media = msxBpb.media; fakeBpb.spfat = msxBpb.spfat; fakeBpb.sptrk = msxBpb.sptrk; fakeBpb.heads = msxBpb.heads; fakeBpb.hsectors = msxBpb.hsectors; fakeBpb.boot_code = msxBpb.boot_code; fakeBpb.boot_signature = msxBpb.boot_signature; fakeBpb.serial_no = msxBpb.serial_no; // TODO: Is there any way to check this? bootable = true; return(BpbKind.Msx); } if (useAtariBpb) { fakeBpb.jump = atariBpb.jump; fakeBpb.oem_name = atariBpb.oem_name; fakeBpb.bps = atariBpb.bps; fakeBpb.spc = atariBpb.spc; fakeBpb.rsectors = atariBpb.rsectors; fakeBpb.fats_no = atariBpb.fats_no; fakeBpb.root_ent = atariBpb.root_ent; fakeBpb.sectors = atariBpb.sectors; fakeBpb.media = atariBpb.media; fakeBpb.spfat = atariBpb.spfat; fakeBpb.sptrk = atariBpb.sptrk; fakeBpb.heads = atariBpb.heads; fakeBpb.boot_code = atariBpb.boot_code; return(BpbKind.Atari); } if (useApricotBpb) { fakeBpb.bps = apricotBpb.mainBPB.bps; fakeBpb.spc = apricotBpb.mainBPB.spc; fakeBpb.rsectors = apricotBpb.mainBPB.rsectors; fakeBpb.fats_no = apricotBpb.mainBPB.fats_no; fakeBpb.root_ent = apricotBpb.mainBPB.root_ent; fakeBpb.sectors = apricotBpb.mainBPB.sectors; fakeBpb.media = apricotBpb.mainBPB.media; fakeBpb.spfat = apricotBpb.mainBPB.spfat; fakeBpb.sptrk = apricotBpb.spt; bootable = apricotBpb.bootType > 0; if (apricotBpb.bootLocation > 0 && apricotBpb.bootLocation + apricotBpb.bootSize < imagePlugin.Info.Sectors) { fakeBpb.boot_code = imagePlugin.ReadSectors(apricotBpb.bootLocation, (uint)(apricotBpb.sectorSize * apricotBpb.bootSize) / imagePlugin.Info.SectorSize); } return(BpbKind.Apricot); } return(BpbKind.None); }
public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, Encoding encoding) { Encoding = Encoding.BigEndianUnicode; information = ""; var vh = new VolumeHeader(); ulong hfspOffset; bool wrapped; uint sectorsToRead = 0x800 / imagePlugin.Info.SectorSize; if (0x800 % imagePlugin.Info.SectorSize > 0) { sectorsToRead++; } byte[] vhSector = imagePlugin.ReadSectors(partition.Start, sectorsToRead); ushort drSigWord = BigEndianBitConverter.ToUInt16(vhSector, 0x400); if (drSigWord == AppleCommon.HFS_MAGIC) // "BD" { drSigWord = BigEndianBitConverter.ToUInt16(vhSector, 0x47C); // Read embedded HFS+ signature if (drSigWord == AppleCommon.HFSP_MAGIC) // "H+" { ushort xdrStABNt = BigEndianBitConverter.ToUInt16(vhSector, 0x47E); uint drAlBlkSiz = BigEndianBitConverter.ToUInt32(vhSector, 0x414); ushort drAlBlSt = BigEndianBitConverter.ToUInt16(vhSector, 0x41C); hfspOffset = (ulong)(((drAlBlSt * 512) + (xdrStABNt * drAlBlkSiz)) / imagePlugin.Info.SectorSize); wrapped = true; } else { hfspOffset = 0; wrapped = false; } } else { hfspOffset = 0; wrapped = false; } vhSector = imagePlugin.ReadSectors(partition.Start + hfspOffset, sectorsToRead); // Read volume header vh.signature = BigEndianBitConverter.ToUInt16(vhSector, 0x400); if (vh.signature != AppleCommon.HFSP_MAGIC && vh.signature != AppleCommon.HFSX_MAGIC) { return; } var sb = new StringBuilder(); if (vh.signature == 0x482B) { sb.AppendLine("HFS+ filesystem."); } if (vh.signature == 0x4858) { sb.AppendLine("HFSX filesystem."); } if (wrapped) { sb.AppendLine("Volume is wrapped inside an HFS volume."); } byte[] tmp = new byte[0x400]; Array.Copy(vhSector, 0x400, tmp, 0, 0x400); vhSector = tmp; vh = Marshal.ByteArrayToStructureBigEndian <VolumeHeader>(vhSector); if (vh.version == 4 || vh.version == 5) { sb.AppendFormat("Filesystem version is {0}.", vh.version).AppendLine(); if ((vh.attributes & 0x80) == 0x80) { sb.AppendLine("Volume is locked on hardware."); } if ((vh.attributes & 0x100) == 0x100) { sb.AppendLine("Volume is unmounted."); } if ((vh.attributes & 0x200) == 0x200) { sb.AppendLine("There are bad blocks in the extents file."); } if ((vh.attributes & 0x400) == 0x400) { sb.AppendLine("Volume does not require cache."); } if ((vh.attributes & 0x800) == 0x800) { sb.AppendLine("Volume state is inconsistent."); } if ((vh.attributes & 0x1000) == 0x1000) { sb.AppendLine("CNIDs are reused."); } if ((vh.attributes & 0x2000) == 0x2000) { sb.AppendLine("Volume is journaled."); } if ((vh.attributes & 0x8000) == 0x8000) { sb.AppendLine("Volume is locked on software."); } sb.AppendFormat("Implementation that last mounted the volume: \"{0}\".", Encoding.ASCII.GetString(vh.lastMountedVersion)).AppendLine(); if ((vh.attributes & 0x2000) == 0x2000) { sb.AppendFormat("Journal starts at allocation block {0}.", vh.journalInfoBlock).AppendLine(); } sb.AppendFormat("Creation date: {0}", DateHandlers.MacToDateTime(vh.createDate)).AppendLine(); sb.AppendFormat("Last modification date: {0}", DateHandlers.MacToDateTime(vh.modifyDate)).AppendLine(); if (vh.backupDate > 0) { sb.AppendFormat("Last backup date: {0}", DateHandlers.MacToDateTime(vh.backupDate)).AppendLine(); } else { sb.AppendLine("Volume has never been backed up"); } if (vh.backupDate > 0) { sb.AppendFormat("Last check date: {0}", DateHandlers.MacToDateTime(vh.checkedDate)).AppendLine(); } else { sb.AppendLine("Volume has never been checked up"); } sb.AppendFormat("{0} files on volume.", vh.fileCount).AppendLine(); sb.AppendFormat("{0} folders on volume.", vh.folderCount).AppendLine(); sb.AppendFormat("{0} bytes per allocation block.", vh.blockSize).AppendLine(); sb.AppendFormat("{0} allocation blocks.", vh.totalBlocks).AppendLine(); sb.AppendFormat("{0} free blocks.", vh.freeBlocks).AppendLine(); sb.AppendFormat("Next allocation block: {0}.", vh.nextAllocation).AppendLine(); sb.AppendFormat("Resource fork clump size: {0} bytes.", vh.rsrcClumpSize).AppendLine(); sb.AppendFormat("Data fork clump size: {0} bytes.", vh.dataClumpSize).AppendLine(); sb.AppendFormat("Next unused CNID: {0}.", vh.nextCatalogID).AppendLine(); sb.AppendFormat("Volume has been mounted writable {0} times.", vh.writeCount).AppendLine(); sb.AppendFormat("Allocation File is {0} bytes.", vh.allocationFile_logicalSize).AppendLine(); sb.AppendFormat("Extents File is {0} bytes.", vh.extentsFile_logicalSize).AppendLine(); sb.AppendFormat("Catalog File is {0} bytes.", vh.catalogFile_logicalSize).AppendLine(); sb.AppendFormat("Attributes File is {0} bytes.", vh.attributesFile_logicalSize).AppendLine(); sb.AppendFormat("Startup File is {0} bytes.", vh.startupFile_logicalSize).AppendLine(); sb.AppendLine("Finder info:"); sb.AppendFormat("CNID of bootable system's directory: {0}", vh.drFndrInfo0).AppendLine(); sb.AppendFormat("CNID of first-run application's directory: {0}", vh.drFndrInfo1).AppendLine(); sb.AppendFormat("CNID of previously opened directory: {0}", vh.drFndrInfo2).AppendLine(); sb.AppendFormat("CNID of bootable Mac OS 8 or 9 directory: {0}", vh.drFndrInfo3).AppendLine(); sb.AppendFormat("CNID of bootable Mac OS X directory: {0}", vh.drFndrInfo5).AppendLine(); if (vh.drFndrInfo6 != 0 && vh.drFndrInfo7 != 0) { sb.AppendFormat("Mac OS X Volume ID: {0:X8}{1:X8}", vh.drFndrInfo6, vh.drFndrInfo7).AppendLine(); } XmlFsType = new FileSystemType(); if (vh.backupDate > 0) { XmlFsType.BackupDate = DateHandlers.MacToDateTime(vh.backupDate); XmlFsType.BackupDateSpecified = true; } XmlFsType.Bootable |= vh.drFndrInfo0 != 0 || vh.drFndrInfo3 != 0 || vh.drFndrInfo5 != 0; XmlFsType.Clusters = vh.totalBlocks; XmlFsType.ClusterSize = vh.blockSize; if (vh.createDate > 0) { XmlFsType.CreationDate = DateHandlers.MacToDateTime(vh.createDate); XmlFsType.CreationDateSpecified = true; } XmlFsType.Dirty = (vh.attributes & 0x100) != 0x100; XmlFsType.Files = vh.fileCount; XmlFsType.FilesSpecified = true; XmlFsType.FreeClusters = vh.freeBlocks; XmlFsType.FreeClustersSpecified = true; if (vh.modifyDate > 0) { XmlFsType.ModificationDate = DateHandlers.MacToDateTime(vh.modifyDate); XmlFsType.ModificationDateSpecified = true; } if (vh.signature == 0x482B) { XmlFsType.Type = "HFS+"; } if (vh.signature == 0x4858) { XmlFsType.Type = "HFSX"; } if (vh.drFndrInfo6 != 0 && vh.drFndrInfo7 != 0) { XmlFsType.VolumeSerial = $"{vh.drFndrInfo6:X8}{vh.drFndrInfo7:X8}"; } XmlFsType.SystemIdentifier = Encoding.ASCII.GetString(vh.lastMountedVersion); } else { sb.AppendFormat("Filesystem version is {0}.", vh.version).AppendLine(); sb.AppendLine("This version is not supported yet."); } information = sb.ToString(); }
public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, Encoding encoding) { Encoding = Encoding.Unicode; information = ""; if (imagePlugin.Info.SectorSize < F2FS_MIN_SECTOR || imagePlugin.Info.SectorSize > F2FS_MAX_SECTOR) { return; } uint sbAddr = F2FS_SUPER_OFFSET / imagePlugin.Info.SectorSize; if (sbAddr == 0) { sbAddr = 1; } uint sbSize = (uint)(Marshal.SizeOf <Superblock>() / imagePlugin.Info.SectorSize); if (Marshal.SizeOf <Superblock>() % imagePlugin.Info.SectorSize != 0) { sbSize++; } byte[] sector = imagePlugin.ReadSectors(partition.Start + sbAddr, sbSize); if (sector.Length < Marshal.SizeOf <Superblock>()) { return; } Superblock f2fsSb = Marshal.ByteArrayToStructureLittleEndian <Superblock>(sector); if (f2fsSb.magic != F2FS_MAGIC) { return; } var sb = new StringBuilder(); sb.AppendLine("F2FS filesystem"); sb.AppendFormat("Version {0}.{1}", f2fsSb.major_ver, f2fsSb.minor_ver).AppendLine(); sb.AppendFormat("{0} bytes per sector", 1 << (int)f2fsSb.log_sectorsize).AppendLine(); sb.AppendFormat("{0} sectors ({1} bytes) per block", 1 << (int)f2fsSb.log_sectors_per_block, 1 << (int)f2fsSb.log_blocksize).AppendLine(); sb.AppendFormat("{0} blocks per segment", f2fsSb.log_blocks_per_seg).AppendLine(); sb.AppendFormat("{0} blocks in volume", f2fsSb.block_count).AppendLine(); sb.AppendFormat("{0} segments per section", f2fsSb.segs_per_sec).AppendLine(); sb.AppendFormat("{0} sections per zone", f2fsSb.secs_per_zone).AppendLine(); sb.AppendFormat("{0} sections", f2fsSb.section_count).AppendLine(); sb.AppendFormat("{0} segments", f2fsSb.segment_count).AppendLine(); sb.AppendFormat("Root directory resides on inode {0}", f2fsSb.root_ino).AppendLine(); sb.AppendFormat("Volume UUID: {0}", f2fsSb.uuid).AppendLine(); sb.AppendFormat("Volume name: {0}", StringHandlers.CToString(f2fsSb.volume_name, Encoding.Unicode, true)). AppendLine(); sb.AppendFormat("Volume last mounted on kernel version: {0}", StringHandlers.CToString(f2fsSb.version)). AppendLine(); sb.AppendFormat("Volume created on kernel version: {0}", StringHandlers.CToString(f2fsSb.init_version)). AppendLine(); information = sb.ToString(); XmlFsType = new FileSystemType { Type = "F2FS filesystem", SystemIdentifier = Encoding.ASCII.GetString(f2fsSb.version), Clusters = f2fsSb.block_count, ClusterSize = (uint)(1 << (int)f2fsSb.log_blocksize), DataPreparerIdentifier = Encoding.ASCII.GetString(f2fsSb.init_version), VolumeName = StringHandlers.CToString(f2fsSb.volume_name, Encoding.Unicode, true), VolumeSerial = f2fsSb.uuid.ToString() }; }
public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, Encoding encoding) { Encoding = encoding ?? Encoding.GetEncoding("iso-8859-15"); information = ""; if (imagePlugin.Info.SectorSize < 512) { return; } Locus_Superblock LocusSb = new Locus_Superblock(); byte[] sector = null; for (ulong location = 0; location <= 8; location++) { uint sbSize = (uint)(Marshal.SizeOf(LocusSb) / imagePlugin.Info.SectorSize); if (Marshal.SizeOf(LocusSb) % imagePlugin.Info.SectorSize != 0) { sbSize++; } sector = imagePlugin.ReadSectors(partition.Start + location, sbSize); if (sector.Length < Marshal.SizeOf(LocusSb)) { return; } IntPtr sbPtr = Marshal.AllocHGlobal(Marshal.SizeOf(LocusSb)); Marshal.Copy(sector, 0, sbPtr, Marshal.SizeOf(LocusSb)); LocusSb = (Locus_Superblock)Marshal.PtrToStructure(sbPtr, typeof(Locus_Superblock)); Marshal.FreeHGlobal(sbPtr); if (LocusSb.s_magic == Locus_Magic || LocusSb.s_magic == Locus_Cigam || LocusSb.s_magic == Locus_OldMagic || LocusSb.s_magic == Locus_OldCigam) { break; } } // We don't care about old version for information if (LocusSb.s_magic != Locus_Magic && LocusSb.s_magic != Locus_Cigam && LocusSb.s_magic != Locus_OldMagic && LocusSb.s_magic != Locus_OldCigam) { return; } // Numerical arrays are not important for information so no need to swap them if (LocusSb.s_magic == Locus_Cigam || LocusSb.s_magic == Locus_OldCigam) { LocusSb = BigEndianMarshal.ByteArrayToStructureBigEndian <Locus_Superblock>(sector); LocusSb.s_flags = (LocusFlags)Swapping.Swap((ushort)LocusSb.s_flags); } StringBuilder sb = new StringBuilder(); sb.AppendLine(LocusSb.s_magic == Locus_OldMagic ? "Locus filesystem (old)" : "Locus filesystem"); int blockSize = LocusSb.s_version == LocusVersion.SB_SB4096 ? 4096 : 1024; string s_fsmnt = StringHandlers.CToString(LocusSb.s_fsmnt, Encoding); string s_fpack = StringHandlers.CToString(LocusSb.s_fpack, Encoding); DicConsole.DebugWriteLine("Locus plugin", "LocusSb.s_magic = 0x{0:X8}", LocusSb.s_magic); DicConsole.DebugWriteLine("Locus plugin", "LocusSb.s_gfs = {0}", LocusSb.s_gfs); DicConsole.DebugWriteLine("Locus plugin", "LocusSb.s_fsize = {0}", LocusSb.s_fsize); DicConsole.DebugWriteLine("Locus plugin", "LocusSb.s_lwm = {0}", LocusSb.s_lwm); DicConsole.DebugWriteLine("Locus plugin", "LocusSb.s_hwm = {0}", LocusSb.s_hwm); DicConsole.DebugWriteLine("Locus plugin", "LocusSb.s_llst = {0}", LocusSb.s_llst); DicConsole.DebugWriteLine("Locus plugin", "LocusSb.s_fstore = {0}", LocusSb.s_fstore); DicConsole.DebugWriteLine("Locus plugin", "LocusSb.s_time = {0}", LocusSb.s_time); DicConsole.DebugWriteLine("Locus plugin", "LocusSb.s_tfree = {0}", LocusSb.s_tfree); DicConsole.DebugWriteLine("Locus plugin", "LocusSb.s_isize = {0}", LocusSb.s_isize); DicConsole.DebugWriteLine("Locus plugin", "LocusSb.s_nfree = {0}", LocusSb.s_nfree); DicConsole.DebugWriteLine("Locus plugin", "LocusSb.s_flags = {0}", LocusSb.s_flags); DicConsole.DebugWriteLine("Locus plugin", "LocusSb.s_tinode = {0}", LocusSb.s_tinode); DicConsole.DebugWriteLine("Locus plugin", "LocusSb.s_lasti = {0}", LocusSb.s_lasti); DicConsole.DebugWriteLine("Locus plugin", "LocusSb.s_nbehind = {0}", LocusSb.s_nbehind); DicConsole.DebugWriteLine("Locus plugin", "LocusSb.s_gfspack = {0}", LocusSb.s_gfspack); DicConsole.DebugWriteLine("Locus plugin", "LocusSb.s_ninode = {0}", LocusSb.s_ninode); DicConsole.DebugWriteLine("Locus plugin", "LocusSb.s_flock = {0}", LocusSb.s_flock); DicConsole.DebugWriteLine("Locus plugin", "LocusSb.s_ilock = {0}", LocusSb.s_ilock); DicConsole.DebugWriteLine("Locus plugin", "LocusSb.s_fmod = {0}", LocusSb.s_fmod); DicConsole.DebugWriteLine("Locus plugin", "LocusSb.s_version = {0}", LocusSb.s_version); sb.AppendFormat("Superblock last modified on {0}", DateHandlers.UnixToDateTime(LocusSb.s_time)) .AppendLine(); sb.AppendFormat("Volume has {0} blocks of {1} bytes each (total {2} bytes)", LocusSb.s_fsize, blockSize, LocusSb.s_fsize * blockSize).AppendLine(); sb.AppendFormat("{0} blocks free ({1} bytes)", LocusSb.s_tfree, LocusSb.s_tfree * blockSize).AppendLine(); sb.AppendFormat("I-node list uses {0} blocks", LocusSb.s_isize).AppendLine(); sb.AppendFormat("{0} free inodes", LocusSb.s_tinode).AppendLine(); sb.AppendFormat("Next free inode search will start at inode {0}", LocusSb.s_lasti).AppendLine(); sb.AppendFormat("There are an estimate of {0} free inodes before next search start", LocusSb.s_nbehind) .AppendLine(); if (LocusSb.s_flags.HasFlag(LocusFlags.SB_RDONLY)) { sb.AppendLine("Read-only volume"); } if (LocusSb.s_flags.HasFlag(LocusFlags.SB_CLEAN)) { sb.AppendLine("Clean volume"); } if (LocusSb.s_flags.HasFlag(LocusFlags.SB_DIRTY)) { sb.AppendLine("Dirty volume"); } if (LocusSb.s_flags.HasFlag(LocusFlags.SB_RMV)) { sb.AppendLine("Removable volume"); } if (LocusSb.s_flags.HasFlag(LocusFlags.SB_PRIMPACK)) { sb.AppendLine("This is the primary pack"); } if (LocusSb.s_flags.HasFlag(LocusFlags.SB_REPLTYPE)) { sb.AppendLine("Replicated volume"); } if (LocusSb.s_flags.HasFlag(LocusFlags.SB_USER)) { sb.AppendLine("User replicated volume"); } if (LocusSb.s_flags.HasFlag(LocusFlags.SB_BACKBONE)) { sb.AppendLine("Backbone volume"); } if (LocusSb.s_flags.HasFlag(LocusFlags.SB_NFS)) { sb.AppendLine("NFS volume"); } if (LocusSb.s_flags.HasFlag(LocusFlags.SB_BYHAND)) { sb.AppendLine("Volume inhibits automatic fsck"); } if (LocusSb.s_flags.HasFlag(LocusFlags.SB_NOSUID)) { sb.AppendLine("Set-uid/set-gid is disabled"); } if (LocusSb.s_flags.HasFlag(LocusFlags.SB_SYNCW)) { sb.AppendLine("Volume uses synchronous writes"); } sb.AppendFormat("Volume label: {0}", s_fsmnt).AppendLine(); sb.AppendFormat("Physical volume name: {0}", s_fpack).AppendLine(); sb.AppendFormat("Global File System number: {0}", LocusSb.s_gfs).AppendLine(); sb.AppendFormat("Global File System pack number {0}", LocusSb.s_gfspack).AppendLine(); information = sb.ToString(); XmlFsType = new FileSystemType { Type = "Locus filesystem", ClusterSize = blockSize, Clusters = LocusSb.s_fsize, // Sometimes it uses one, or the other. Use the bigger VolumeName = string.IsNullOrEmpty(s_fsmnt) ? s_fpack : s_fsmnt, ModificationDate = DateHandlers.UnixToDateTime(LocusSb.s_time), ModificationDateSpecified = true, Dirty = !LocusSb.s_flags.HasFlag(LocusFlags.SB_CLEAN) || LocusSb.s_flags.HasFlag(LocusFlags.SB_DIRTY), FreeClusters = LocusSb.s_tfree, FreeClustersSpecified = true }; }