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; }
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 Errno Mount(IMediaImage imagePlugin, Partition partition, Encoding encoding, Dictionary <string, string> options, string @namespace) { 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 MasterDirectoryBlock(); mdbBlocks = device.ReadSector(2 + partitionStart); bootBlocks = device.ReadSector(0 + partitionStart); 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 = (AppleCommon.VolumeAttributes)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 != AppleCommon.BB_MAGIC) { bootBlocks = null; } XmlFsType = new FileSystemType(); if (volMDB.drLsBkUp > 0) { XmlFsType.BackupDate = DateHandlers.MacToDateTime(volMDB.drLsBkUp); XmlFsType.BackupDateSpecified = true; } XmlFsType.Bootable = bbSig == AppleCommon.BB_MAGIC; XmlFsType.Clusters = volMDB.drNmAlBlks; XmlFsType.ClusterSize = 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); }