public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, Encoding encoding) { Encoding = encoding ?? Encoding.GetEncoding("macintosh"); information = ""; var sb = new StringBuilder(); byte[] bbSector = null; byte[] mdbSector = null; ushort drSigWord; bool apmFromHddOnCd = false; if (imagePlugin.Info.SectorSize == 2352 || imagePlugin.Info.SectorSize == 2448 || imagePlugin.Info.SectorSize == 2048) { byte[] tmpSector = imagePlugin.ReadSectors(partition.Start, 2); foreach (int offset in new[] { 0, 0x200, 0x400, 0x600, 0x800, 0xA00 }) { drSigWord = BigEndianBitConverter.ToUInt16(tmpSector, offset); if (drSigWord != AppleCommon.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 == AppleCommon.HFS_MAGIC) { bbSector = imagePlugin.ReadSector(partition.Start); } else { return; } } MasterDirectoryBlock mdb = Marshal.ByteArrayToStructureBigEndian <MasterDirectoryBlock>(mdbSector); 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.HasFlag(AppleCommon.VolumeAttributes.HardwareLock)) { sb.AppendLine("Volume is locked by hardware."); } sb.AppendLine(mdb.drAtrb.HasFlag(AppleCommon.VolumeAttributes.Unmounted) ? "Volume was unmonted." : "Volume is mounted."); if (mdb.drAtrb.HasFlag(AppleCommon.VolumeAttributes.SparedBadBlocks)) { sb.AppendLine("Volume has spared bad blocks."); } if (mdb.drAtrb.HasFlag(AppleCommon.VolumeAttributes.DoesNotNeedCache)) { sb.AppendLine("Volume does not need cache."); } if (mdb.drAtrb.HasFlag(AppleCommon.VolumeAttributes.BootInconsistent)) { sb.AppendLine("Boot volume is inconsistent."); } if (mdb.drAtrb.HasFlag(AppleCommon.VolumeAttributes.ReusedIds)) { sb.AppendLine("There are reused CNIDs."); } if (mdb.drAtrb.HasFlag(AppleCommon.VolumeAttributes.Journaled)) { sb.AppendLine("Volume is journaled."); } if (mdb.drAtrb.HasFlag(AppleCommon.VolumeAttributes.Inconsistent)) { sb.AppendLine("Volume is seriously inconsistent."); } if (mdb.drAtrb.HasFlag(AppleCommon.VolumeAttributes.SoftwareLock)) { 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 == AppleCommon.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(); } string bootBlockInfo = AppleCommon.GetBootBlockInformation(bbSector, Encoding); if (bootBlockInfo != null) { sb.AppendLine("Volume is bootable."); sb.AppendLine(); sb.AppendLine(bootBlockInfo); } 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 = bootBlockInfo != null || mdb.drFndrInfo0 != 0 || mdb.drFndrInfo3 != 0 || mdb.drFndrInfo5 != 0; XmlFsType.Clusters = mdb.drNmAlBlks; XmlFsType.ClusterSize = mdb.drAlBlkSiz; if (mdb.drCrDate > 0) { XmlFsType.CreationDate = DateHandlers.MacToDateTime(mdb.drCrDate); XmlFsType.CreationDateSpecified = true; } XmlFsType.Dirty = !mdb.drAtrb.HasFlag(AppleCommon.VolumeAttributes.Unmounted); 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 void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, Encoding encoding) { Encoding = encoding ?? new MacRoman(); information = ""; var sb = new StringBuilder(); var mdb = new MasterDirectoryBlock(); byte[] mdbSector = imagePlugin.ReadSector(2 + partition.Start); byte[] bbSector = imagePlugin.ReadSector(0 + partition.Start); mdb.drSigWord = BigEndianBitConverter.ToUInt16(mdbSector, 0x000); if (mdb.drSigWord != MFS_MAGIC) { return; } mdb.drCrDate = BigEndianBitConverter.ToUInt32(mdbSector, 0x002); mdb.drLsBkUp = BigEndianBitConverter.ToUInt32(mdbSector, 0x006); mdb.drAtrb = (AppleCommon.VolumeAttributes)BigEndianBitConverter.ToUInt16(mdbSector, 0x00A); mdb.drNmFls = BigEndianBitConverter.ToUInt16(mdbSector, 0x00C); mdb.drDirSt = BigEndianBitConverter.ToUInt16(mdbSector, 0x00E); mdb.drBlLen = BigEndianBitConverter.ToUInt16(mdbSector, 0x010); mdb.drNmAlBlks = BigEndianBitConverter.ToUInt16(mdbSector, 0x012); mdb.drAlBlkSiz = BigEndianBitConverter.ToUInt32(mdbSector, 0x014); mdb.drClpSiz = BigEndianBitConverter.ToUInt32(mdbSector, 0x018); mdb.drAlBlSt = BigEndianBitConverter.ToUInt16(mdbSector, 0x01C); mdb.drNxtFNum = BigEndianBitConverter.ToUInt32(mdbSector, 0x01E); mdb.drFreeBks = BigEndianBitConverter.ToUInt16(mdbSector, 0x022); mdb.drVNSiz = mdbSector[0x024]; byte[] variableSize = new byte[mdb.drVNSiz + 1]; Array.Copy(mdbSector, 0x024, variableSize, 0, mdb.drVNSiz + 1); mdb.drVN = StringHandlers.PascalToString(variableSize, Encoding); sb.AppendLine("Apple Macintosh File System"); sb.AppendLine(); sb.AppendLine("Master Directory Block:"); sb.AppendFormat("Creation date: {0}", DateHandlers.MacToDateTime(mdb.drCrDate)).AppendLine(); sb.AppendFormat("Last backup date: {0}", DateHandlers.MacToDateTime(mdb.drLsBkUp)).AppendLine(); if (mdb.drAtrb.HasFlag(AppleCommon.VolumeAttributes.HardwareLock)) { sb.AppendLine("Volume is locked by hardware."); } sb.AppendLine(mdb.drAtrb.HasFlag(AppleCommon.VolumeAttributes.Unmounted) ? "Volume was unmonted." : "Volume is mounted."); if (mdb.drAtrb.HasFlag(AppleCommon.VolumeAttributes.SparedBadBlocks)) { sb.AppendLine("Volume has spared bad blocks."); } if (mdb.drAtrb.HasFlag(AppleCommon.VolumeAttributes.DoesNotNeedCache)) { sb.AppendLine("Volume does not need cache."); } if (mdb.drAtrb.HasFlag(AppleCommon.VolumeAttributes.BootInconsistent)) { sb.AppendLine("Boot volume is inconsistent."); } if (mdb.drAtrb.HasFlag(AppleCommon.VolumeAttributes.ReusedIds)) { sb.AppendLine("There are reused CNIDs."); } if (mdb.drAtrb.HasFlag(AppleCommon.VolumeAttributes.Inconsistent)) { sb.AppendLine("Volume is seriously inconsistent."); } if (mdb.drAtrb.HasFlag(AppleCommon.VolumeAttributes.SoftwareLock)) { sb.AppendLine("Volume is locked by software."); } sb.AppendFormat("{0} files on volume", mdb.drNmFls).AppendLine(); sb.AppendFormat("First directory sector: {0}", mdb.drDirSt).AppendLine(); sb.AppendFormat("{0} sectors in directory.", mdb.drBlLen).AppendLine(); sb.AppendFormat("{0} volume allocation blocks.", mdb.drNmAlBlks + 1).AppendLine(); sb.AppendFormat("Size of allocation blocks: {0} bytes", mdb.drAlBlkSiz).AppendLine(); sb.AppendFormat("{0} bytes to allocate.", mdb.drClpSiz).AppendLine(); sb.AppendFormat("First allocation block (#2) starts in sector {0}.", mdb.drAlBlSt).AppendLine(); sb.AppendFormat("Next unused file number: {0}", mdb.drNxtFNum).AppendLine(); sb.AppendFormat("{0} unused allocation blocks.", mdb.drFreeBks).AppendLine(); sb.AppendFormat("Volume name: {0}", mdb.drVN).AppendLine(); string bootBlockInfo = AppleCommon.GetBootBlockInformation(bbSector, Encoding); if (bootBlockInfo != null) { sb.AppendLine("Volume is bootable."); sb.AppendLine(); sb.AppendLine(bootBlockInfo); } else { sb.AppendLine("Volume is not bootable."); } information = sb.ToString(); XmlFsType = new FileSystemType(); if (mdb.drLsBkUp > 0) { XmlFsType.BackupDate = DateHandlers.MacToDateTime(mdb.drLsBkUp); XmlFsType.BackupDateSpecified = true; } XmlFsType.Bootable = bootBlockInfo != null; XmlFsType.Clusters = mdb.drNmAlBlks; XmlFsType.ClusterSize = mdb.drAlBlkSiz; if (mdb.drCrDate > 0) { XmlFsType.CreationDate = DateHandlers.MacToDateTime(mdb.drCrDate); XmlFsType.CreationDateSpecified = true; } XmlFsType.Files = mdb.drNmFls; XmlFsType.FilesSpecified = true; XmlFsType.FreeClusters = mdb.drFreeBks; XmlFsType.FreeClustersSpecified = true; XmlFsType.Type = "MFS"; XmlFsType.VolumeName = mdb.drVN; }