public void Open(string path) { // Prepend data fork name with "R." string ProDosAppleDouble; // Prepend data fork name with '%' string UNIXAppleDouble; // Change file extension to ADF string DOSAppleDouble; // Change file extension to adf string DOSAppleDoubleLower; // Store AppleDouble header file in ".AppleDouble" folder with same name string NetatalkAppleDouble; // Store AppleDouble header file in "resource.frk" folder with same name string DAVEAppleDouble; // Prepend data fork name with "._" string OSXAppleDouble; // Adds ".rsrc" extension string UnArAppleDouble; string filename = Path.GetFileName(path); string filenameNoExt = Path.GetFileNameWithoutExtension(path); string parentFolder = Path.GetDirectoryName(path); ProDosAppleDouble = Path.Combine(parentFolder ?? throw new InvalidOperationException(), "R." + filename); UNIXAppleDouble = Path.Combine(parentFolder, "%" + filename); DOSAppleDouble = Path.Combine(parentFolder, filenameNoExt + ".ADF"); DOSAppleDoubleLower = Path.Combine(parentFolder, filenameNoExt + ".adf"); NetatalkAppleDouble = Path.Combine(parentFolder, ".AppleDouble", filename ?? throw new InvalidOperationException()); DAVEAppleDouble = Path.Combine(parentFolder, "resource.frk", filename); OSXAppleDouble = Path.Combine(parentFolder, "._" + filename); UnArAppleDouble = Path.Combine(parentFolder, filename + ".rsrc"); // Check AppleDouble created by A/UX in ProDOS filesystem if (File.Exists(ProDosAppleDouble)) { FileStream prodosStream = new FileStream(ProDosAppleDouble, FileMode.Open, FileAccess.Read); if (prodosStream.Length > 26) { byte[] prodos_b = new byte[26]; prodosStream.Read(prodos_b, 0, 26); header = BigEndianMarshal.ByteArrayToStructureBigEndian <AppleDoubleHeader>(prodos_b); prodosStream.Close(); if (header.magic == AppleDoubleMagic && (header.version == AppleDoubleVersion || header.version == AppleDoubleVersion2)) { headerPath = ProDosAppleDouble; } } } // Check AppleDouble created by A/UX in UFS filesystem if (File.Exists(UNIXAppleDouble)) { FileStream unixStream = new FileStream(UNIXAppleDouble, FileMode.Open, FileAccess.Read); if (unixStream.Length > 26) { byte[] unix_b = new byte[26]; unixStream.Read(unix_b, 0, 26); header = BigEndianMarshal.ByteArrayToStructureBigEndian <AppleDoubleHeader>(unix_b); unixStream.Close(); if (header.magic == AppleDoubleMagic && (header.version == AppleDoubleVersion || header.version == AppleDoubleVersion2)) { headerPath = UNIXAppleDouble; } } } // Check AppleDouble created by A/UX in FAT filesystem if (File.Exists(DOSAppleDouble)) { FileStream dosStream = new FileStream(DOSAppleDouble, FileMode.Open, FileAccess.Read); if (dosStream.Length > 26) { byte[] dos_b = new byte[26]; dosStream.Read(dos_b, 0, 26); header = BigEndianMarshal.ByteArrayToStructureBigEndian <AppleDoubleHeader>(dos_b); dosStream.Close(); if (header.magic == AppleDoubleMagic && (header.version == AppleDoubleVersion || header.version == AppleDoubleVersion2)) { headerPath = DOSAppleDouble; } } } // Check AppleDouble created by A/UX in case preserving FAT filesystem if (File.Exists(DOSAppleDoubleLower)) { FileStream doslStream = new FileStream(DOSAppleDoubleLower, FileMode.Open, FileAccess.Read); if (doslStream.Length > 26) { byte[] dosl_b = new byte[26]; doslStream.Read(dosl_b, 0, 26); header = BigEndianMarshal.ByteArrayToStructureBigEndian <AppleDoubleHeader>(dosl_b); doslStream.Close(); if (header.magic == AppleDoubleMagic && (header.version == AppleDoubleVersion || header.version == AppleDoubleVersion2)) { headerPath = DOSAppleDoubleLower; } } } // Check AppleDouble created by Netatalk if (File.Exists(NetatalkAppleDouble)) { FileStream netatalkStream = new FileStream(NetatalkAppleDouble, FileMode.Open, FileAccess.Read); if (netatalkStream.Length > 26) { byte[] netatalk_b = new byte[26]; netatalkStream.Read(netatalk_b, 0, 26); header = BigEndianMarshal.ByteArrayToStructureBigEndian <AppleDoubleHeader>(netatalk_b); netatalkStream.Close(); if (header.magic == AppleDoubleMagic && (header.version == AppleDoubleVersion || header.version == AppleDoubleVersion2)) { headerPath = NetatalkAppleDouble; } } } // Check AppleDouble created by DAVE if (File.Exists(DAVEAppleDouble)) { FileStream daveStream = new FileStream(DAVEAppleDouble, FileMode.Open, FileAccess.Read); if (daveStream.Length > 26) { byte[] dave_b = new byte[26]; daveStream.Read(dave_b, 0, 26); header = BigEndianMarshal.ByteArrayToStructureBigEndian <AppleDoubleHeader>(dave_b); daveStream.Close(); if (header.magic == AppleDoubleMagic && (header.version == AppleDoubleVersion || header.version == AppleDoubleVersion2)) { headerPath = DAVEAppleDouble; } } } // Check AppleDouble created by Mac OS X if (File.Exists(OSXAppleDouble)) { FileStream osxStream = new FileStream(OSXAppleDouble, FileMode.Open, FileAccess.Read); if (osxStream.Length > 26) { byte[] osx_b = new byte[26]; osxStream.Read(osx_b, 0, 26); header = BigEndianMarshal.ByteArrayToStructureBigEndian <AppleDoubleHeader>(osx_b); osxStream.Close(); if (header.magic == AppleDoubleMagic && (header.version == AppleDoubleVersion || header.version == AppleDoubleVersion2)) { headerPath = OSXAppleDouble; } } } // Check AppleDouble created by UnAr (from The Unarchiver) if (File.Exists(UnArAppleDouble)) { FileStream unarStream = new FileStream(UnArAppleDouble, FileMode.Open, FileAccess.Read); if (unarStream.Length > 26) { byte[] unar_b = new byte[26]; unarStream.Read(unar_b, 0, 26); header = BigEndianMarshal.ByteArrayToStructureBigEndian <AppleDoubleHeader>(unar_b); unarStream.Close(); if (header.magic == AppleDoubleMagic && (header.version == AppleDoubleVersion || header.version == AppleDoubleVersion2)) { headerPath = UnArAppleDouble; } } } FileStream fs = new FileStream(headerPath, FileMode.Open, FileAccess.Read); fs.Seek(0, SeekOrigin.Begin); byte[] hdr_b = new byte[26]; fs.Read(hdr_b, 0, 26); header = BigEndianMarshal.ByteArrayToStructureBigEndian <AppleDoubleHeader>(hdr_b); AppleDoubleEntry[] entries = new AppleDoubleEntry[header.entries]; for (int i = 0; i < header.entries; i++) { byte[] entry = new byte[12]; fs.Read(entry, 0, 12); entries[i] = BigEndianMarshal.ByteArrayToStructureBigEndian <AppleDoubleEntry>(entry); } creationTime = DateTime.UtcNow; lastWriteTime = creationTime; foreach (AppleDoubleEntry entry in entries) { switch ((AppleDoubleEntryID)entry.id) { case AppleDoubleEntryID.DataFork: // AppleDouble have datafork in separated file break; case AppleDoubleEntryID.FileDates: fs.Seek(entry.offset, SeekOrigin.Begin); byte[] dates_b = new byte[16]; fs.Read(dates_b, 0, 16); AppleDoubleFileDates dates = BigEndianMarshal.ByteArrayToStructureBigEndian <AppleDoubleFileDates>(dates_b); creationTime = DateHandlers.UnixUnsignedToDateTime(dates.creationDate); lastWriteTime = DateHandlers.UnixUnsignedToDateTime(dates.modificationDate); break; case AppleDoubleEntryID.FileInfo: fs.Seek(entry.offset, SeekOrigin.Begin); byte[] finfo = new byte[entry.length]; fs.Read(finfo, 0, finfo.Length); if (MacintoshHome.SequenceEqual(header.homeFilesystem)) { AppleDoubleMacFileInfo macinfo = BigEndianMarshal.ByteArrayToStructureBigEndian <AppleDoubleMacFileInfo>(finfo); creationTime = DateHandlers.MacToDateTime(macinfo.creationDate); lastWriteTime = DateHandlers.MacToDateTime(macinfo.modificationDate); } else if (ProDOSHome.SequenceEqual(header.homeFilesystem)) { AppleDoubleProDOSFileInfo prodosinfo = BigEndianMarshal.ByteArrayToStructureBigEndian <AppleDoubleProDOSFileInfo>(finfo); creationTime = DateHandlers.MacToDateTime(prodosinfo.creationDate); lastWriteTime = DateHandlers.MacToDateTime(prodosinfo.modificationDate); } else if (UNIXHome.SequenceEqual(header.homeFilesystem)) { AppleDoubleUNIXFileInfo unixinfo = BigEndianMarshal.ByteArrayToStructureBigEndian <AppleDoubleUNIXFileInfo>(finfo); creationTime = DateHandlers.UnixUnsignedToDateTime(unixinfo.creationDate); lastWriteTime = DateHandlers.UnixUnsignedToDateTime(unixinfo.modificationDate); } else if (DOSHome.SequenceEqual(header.homeFilesystem)) { AppleDoubleDOSFileInfo dosinfo = BigEndianMarshal.ByteArrayToStructureBigEndian <AppleDoubleDOSFileInfo>(finfo); lastWriteTime = DateHandlers.DosToDateTime(dosinfo.modificationDate, dosinfo.modificationTime); } break; case AppleDoubleEntryID.ResourceFork: rsrcFork = entry; break; } } dataFork = new AppleDoubleEntry { id = (uint)AppleDoubleEntryID.DataFork }; if (File.Exists(path)) { FileStream dataFs = new FileStream(path, FileMode.Open, FileAccess.Read); dataFork.length = (uint)dataFs.Length; dataFs.Close(); } fs.Close(); opened = true; basePath = path; }
public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, Encoding encoding) { Encoding = Encoding.BigEndianUnicode; information = ""; HfsPlusVolumeHeader vh = new HfsPlusVolumeHeader(); 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 == HFS_MAGIC) // "BD" { drSigWord = BigEndianBitConverter.ToUInt16(vhSector, 0x47C); // Read embedded HFS+ signature if (drSigWord == 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 != HFSP_MAGIC && vh.signature != HFSX_MAGIC) { return; } StringBuilder 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 <HfsPlusVolumeHeader>(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 Open(string path) { FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read); fs.Seek(0, SeekOrigin.Begin); byte[] hdr_b = new byte[26]; fs.Read(hdr_b, 0, 26); header = BigEndianMarshal.ByteArrayToStructureBigEndian <AppleSingleHeader>(hdr_b); AppleSingleEntry[] entries = new AppleSingleEntry[header.entries]; for (int i = 0; i < header.entries; i++) { byte[] entry = new byte[12]; fs.Read(entry, 0, 12); entries[i] = BigEndianMarshal.ByteArrayToStructureBigEndian <AppleSingleEntry>(entry); } creationTime = DateTime.UtcNow; lastWriteTime = creationTime; foreach (AppleSingleEntry entry in entries) { switch ((AppleSingleEntryID)entry.id) { case AppleSingleEntryID.DataFork: dataFork = entry; break; case AppleSingleEntryID.FileDates: fs.Seek(entry.offset, SeekOrigin.Begin); byte[] dates_b = new byte[16]; fs.Read(dates_b, 0, 16); AppleSingleFileDates dates = BigEndianMarshal.ByteArrayToStructureBigEndian <AppleSingleFileDates>(dates_b); creationTime = DateHandlers.MacToDateTime(dates.creationDate); lastWriteTime = DateHandlers.MacToDateTime(dates.modificationDate); break; case AppleSingleEntryID.FileInfo: fs.Seek(entry.offset, SeekOrigin.Begin); byte[] finfo = new byte[entry.length]; fs.Read(finfo, 0, finfo.Length); if (MacintoshHome.SequenceEqual(header.homeFilesystem)) { AppleSingleMacFileInfo macinfo = BigEndianMarshal.ByteArrayToStructureBigEndian <AppleSingleMacFileInfo>(finfo); creationTime = DateHandlers.MacToDateTime(macinfo.creationDate); lastWriteTime = DateHandlers.MacToDateTime(macinfo.modificationDate); } else if (ProDOSHome.SequenceEqual(header.homeFilesystem)) { AppleSingleProDOSFileInfo prodosinfo = BigEndianMarshal.ByteArrayToStructureBigEndian <AppleSingleProDOSFileInfo>(finfo); creationTime = DateHandlers.MacToDateTime(prodosinfo.creationDate); lastWriteTime = DateHandlers.MacToDateTime(prodosinfo.modificationDate); } else if (UNIXHome.SequenceEqual(header.homeFilesystem)) { AppleSingleUNIXFileInfo unixinfo = BigEndianMarshal.ByteArrayToStructureBigEndian <AppleSingleUNIXFileInfo>(finfo); creationTime = DateHandlers.UnixUnsignedToDateTime(unixinfo.creationDate); lastWriteTime = DateHandlers.UnixUnsignedToDateTime(unixinfo.modificationDate); } else if (DOSHome.SequenceEqual(header.homeFilesystem)) { AppleSingleDOSFileInfo dosinfo = BigEndianMarshal.ByteArrayToStructureBigEndian <AppleSingleDOSFileInfo>(finfo); lastWriteTime = DateHandlers.DosToDateTime(dosinfo.modificationDate, dosinfo.modificationTime); } break; case AppleSingleEntryID.ResourceFork: rsrcFork = entry; break; } } fs.Close(); opened = true; isPath = true; basePath = path; }
public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, Encoding encoding) { Encoding = encoding ?? new MacRoman(); information = ""; StringBuilder sb = new StringBuilder(); MFS_MasterDirectoryBlock MDB = new MFS_MasterDirectoryBlock(); MFS_BootBlock BB = new MFS_BootBlock(); byte[] pString = new byte[16]; byte[] mdbSector = imagePlugin.ReadSector(2 + partition.Start); byte[] bbSector = imagePlugin.ReadSector(0 + partition.Start); BigEndianBitConverter.IsLittleEndian = BitConverter.IsLittleEndian; 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 = 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); BB.signature = BigEndianBitConverter.ToUInt16(bbSector, 0x000); if (BB.signature == MFSBB_MAGIC) { BB.branch = BigEndianBitConverter.ToUInt32(bbSector, 0x002); BB.boot_flags = bbSector[0x006]; BB.boot_version = bbSector[0x007]; BB.sec_sv_pages = BigEndianBitConverter.ToInt16(bbSector, 0x008); Array.Copy(mdbSector, 0x00A, pString, 0, 16); BB.system_name = StringHandlers.PascalToString(pString, Encoding); Array.Copy(mdbSector, 0x01A, pString, 0, 16); BB.finder_name = StringHandlers.PascalToString(pString, Encoding); Array.Copy(mdbSector, 0x02A, pString, 0, 16); BB.debug_name = StringHandlers.PascalToString(pString, Encoding); Array.Copy(mdbSector, 0x03A, pString, 0, 16); BB.disasm_name = StringHandlers.PascalToString(pString, Encoding); Array.Copy(mdbSector, 0x04A, pString, 0, 16); BB.stupscr_name = StringHandlers.PascalToString(pString, Encoding); Array.Copy(mdbSector, 0x05A, pString, 0, 16); BB.bootup_name = StringHandlers.PascalToString(pString, Encoding); Array.Copy(mdbSector, 0x06A, pString, 0, 16); BB.clipbrd_name = StringHandlers.PascalToString(pString, Encoding); BB.max_files = BigEndianBitConverter.ToUInt16(bbSector, 0x07A); BB.queue_size = BigEndianBitConverter.ToUInt16(bbSector, 0x07C); BB.heap_128k = BigEndianBitConverter.ToUInt32(bbSector, 0x07E); BB.heap_256k = BigEndianBitConverter.ToUInt32(bbSector, 0x082); BB.heap_512k = BigEndianBitConverter.ToUInt32(bbSector, 0x086); } else { BB.signature = 0x0000; } 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 & 0x80) == 0x80) { sb.AppendLine("Volume is locked by hardware."); } if ((MDB.drAtrb & 0x8000) == 0x8000) { 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(); if (BB.signature == MFSBB_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.sec_sv_pages > 0) { sb.AppendLine("Allocate secondary sound buffer at boot."); } else if (BB.sec_sv_pages < 0) { sb.AppendLine("Allocate secondary sound and video buffers at boot."); } sb.AppendFormat("System filename: {0}", BB.system_name).AppendLine(); sb.AppendFormat("Finder filename: {0}", BB.finder_name).AppendLine(); sb.AppendFormat("Debugger filename: {0}", BB.debug_name).AppendLine(); sb.AppendFormat("Disassembler filename: {0}", BB.disasm_name).AppendLine(); sb.AppendFormat("Startup screen filename: {0}", BB.stupscr_name).AppendLine(); sb.AppendFormat("First program to execute at boot: {0}", BB.bootup_name).AppendLine(); sb.AppendFormat("Clipboard filename: {0}", BB.clipbrd_name).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 { 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 = BB.signature == MFSBB_MAGIC; XmlFsType.Clusters = MDB.drNmAlBlks; XmlFsType.ClusterSize = (int)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 = ""; 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; } } HFS_MasterDirectoryBlock MDB = BigEndianMarshal.ByteArrayToStructureBigEndian <HFS_MasterDirectoryBlock>(mdbSector); HFS_BootBlock BB = BigEndianMarshal.ByteArrayToStructureBigEndian <HFS_BootBlock>(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}"; } }
bool FillDirectory() { idToFilename = new Dictionary <uint, string>(); idToEntry = new Dictionary <uint, MFS_FileEntry>(); filenameToId = new Dictionary <string, uint>(); int offset = 0; while (offset + 51 < directoryBlocks.Length) { MFS_FileEntry entry = new MFS_FileEntry { flUsrWds = new byte[16], flFlags = (MFS_FileFlags)directoryBlocks[offset + 0] }; if (!entry.flFlags.HasFlag(MFS_FileFlags.Used)) { break; } entry.flTyp = directoryBlocks[offset + 1]; Array.Copy(directoryBlocks, offset + 2, entry.flUsrWds, 0, 16); entry.flFlNum = BigEndianBitConverter.ToUInt32(directoryBlocks, offset + 18); entry.flStBlk = BigEndianBitConverter.ToUInt16(directoryBlocks, offset + 22); entry.flLgLen = BigEndianBitConverter.ToUInt32(directoryBlocks, offset + 24); entry.flPyLen = BigEndianBitConverter.ToUInt32(directoryBlocks, offset + 28); entry.flRStBlk = BigEndianBitConverter.ToUInt16(directoryBlocks, offset + 32); entry.flRLgLen = BigEndianBitConverter.ToUInt32(directoryBlocks, offset + 34); entry.flRPyLen = BigEndianBitConverter.ToUInt32(directoryBlocks, offset + 38); entry.flCrDat = BigEndianBitConverter.ToUInt32(directoryBlocks, offset + 42); entry.flMdDat = BigEndianBitConverter.ToUInt32(directoryBlocks, offset + 46); entry.flNam = new byte[directoryBlocks[offset + 50] + 1]; Array.Copy(directoryBlocks, offset + 50, entry.flNam, 0, entry.flNam.Length); string lowerFilename = StringHandlers .PascalToString(entry.flNam, Encoding).ToLowerInvariant().Replace('/', ':'); if (entry.flFlags.HasFlag(MFS_FileFlags.Used) && !idToFilename.ContainsKey(entry.flFlNum) && !idToEntry.ContainsKey(entry.flFlNum) && !filenameToId.ContainsKey(lowerFilename) && entry.flFlNum > 0) { idToEntry.Add(entry.flFlNum, entry); idToFilename.Add(entry.flFlNum, StringHandlers.PascalToString(entry.flNam, Encoding).Replace('/', ':')); filenameToId.Add(lowerFilename, entry.flFlNum); DicConsole.DebugWriteLine("DEBUG (AppleMFS plugin)", "entry.flFlags = {0}", entry.flFlags); DicConsole.DebugWriteLine("DEBUG (AppleMFS plugin)", "entry.flTyp = {0}", entry.flTyp); DicConsole.DebugWriteLine("DEBUG (AppleMFS plugin)", "entry.flFlNum = {0}", entry.flFlNum); DicConsole.DebugWriteLine("DEBUG (AppleMFS plugin)", "entry.flStBlk = {0}", entry.flStBlk); DicConsole.DebugWriteLine("DEBUG (AppleMFS plugin)", "entry.flLgLen = {0}", entry.flLgLen); DicConsole.DebugWriteLine("DEBUG (AppleMFS plugin)", "entry.flPyLen = {0}", entry.flPyLen); DicConsole.DebugWriteLine("DEBUG (AppleMFS plugin)", "entry.flRStBlk = {0}", entry.flRStBlk); DicConsole.DebugWriteLine("DEBUG (AppleMFS plugin)", "entry.flRLgLen = {0}", entry.flRLgLen); DicConsole.DebugWriteLine("DEBUG (AppleMFS plugin)", "entry.flRPyLen = {0}", entry.flRPyLen); DicConsole.DebugWriteLine("DEBUG (AppleMFS plugin)", "entry.flCrDat = {0}", DateHandlers.MacToDateTime(entry.flCrDat)); DicConsole.DebugWriteLine("DEBUG (AppleMFS plugin)", "entry.flMdDat = {0}", DateHandlers.MacToDateTime(entry.flMdDat)); DicConsole.DebugWriteLine("DEBUG (AppleMFS plugin)", "entry.flNam0 = {0}", StringHandlers.PascalToString(entry.flNam, Encoding)); } offset += 50 + entry.flNam.Length; // "Entries are always an integral number of words" if (offset % 2 != 0) { offset++; } // TODO: "Entries don't cross logical block boundaries" } return(true); }
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"); 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); 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); }
public Errno Stat(string path, out FileEntryInfo stat) { stat = null; if (!_mounted) { return(Errno.AccessDenied); } string[] pathElements = path.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries); if (pathElements.Length != 1) { return(Errno.NotSupported); } path = pathElements[0]; if (_debug) { if (string.Compare(path, "$", StringComparison.InvariantCulture) == 0 || string.Compare(path, "$Boot", StringComparison.InvariantCulture) == 0 || string.Compare(path, "$Bitmap", StringComparison.InvariantCulture) == 0 || string.Compare(path, "$MDB", StringComparison.InvariantCulture) == 0) { stat = new FileEntryInfo { BlockSize = _device.Info.SectorSize, Inode = 0, Links = 1, Attributes = FileAttributes.System }; if (string.Compare(path, "$", StringComparison.InvariantCulture) == 0) { stat.Blocks = (_directoryBlocks.Length / stat.BlockSize) + (_directoryBlocks.Length % stat.BlockSize); stat.Length = _directoryBlocks.Length; } else if (string.Compare(path, "$Bitmap", StringComparison.InvariantCulture) == 0) { stat.Blocks = (_blockMapBytes.Length / stat.BlockSize) + (_blockMapBytes.Length % stat.BlockSize); stat.Length = _blockMapBytes.Length; } else if (string.Compare(path, "$Boot", StringComparison.InvariantCulture) == 0 && _bootBlocks != null) { stat.Blocks = (_bootBlocks.Length / stat.BlockSize) + (_bootBlocks.Length % stat.BlockSize); stat.Length = _bootBlocks.Length; } else if (string.Compare(path, "$MDB", StringComparison.InvariantCulture) == 0) { stat.Blocks = (_mdbBlocks.Length / stat.BlockSize) + (_mdbBlocks.Length % stat.BlockSize); stat.Length = _mdbBlocks.Length; } else { return(Errno.InvalidArgument); } return(Errno.NoError); } } if (!_filenameToId.TryGetValue(path.ToLowerInvariant(), out uint fileId)) { return(Errno.NoSuchFile); } if (!_idToEntry.TryGetValue(fileId, out FileEntry entry)) { return(Errno.NoSuchFile); } Errno error = GetAttributes(path, out FileAttributes attr); if (error != Errno.NoError) { return(error); } stat = new FileEntryInfo { Attributes = attr, Blocks = entry.flLgLen / _volMdb.drAlBlkSiz, BlockSize = _volMdb.drAlBlkSiz, CreationTime = DateHandlers.MacToDateTime(entry.flCrDat), Inode = entry.flFlNum, LastWriteTime = DateHandlers.MacToDateTime(entry.flMdDat), Length = entry.flPyLen, Links = 1 }; return(Errno.NoError); }
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[] pString = new byte[16]; 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 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 = 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 void Open(Stream stream) { stream.Seek(0, SeekOrigin.Begin); byte[] hdrB = new byte[26]; stream.Read(hdrB, 0, 26); _header = Marshal.ByteArrayToStructureBigEndian <AppleSingleHeader>(hdrB); AppleSingleEntry[] entries = new AppleSingleEntry[_header.entries]; for (int i = 0; i < _header.entries; i++) { byte[] entry = new byte[12]; stream.Read(entry, 0, 12); entries[i] = Marshal.ByteArrayToStructureBigEndian <AppleSingleEntry>(entry); } _creationTime = DateTime.UtcNow; _lastWriteTime = _creationTime; foreach (AppleSingleEntry entry in entries) { switch ((AppleSingleEntryID)entry.id) { case AppleSingleEntryID.DataFork: _dataFork = entry; break; case AppleSingleEntryID.FileDates: stream.Seek(entry.offset, SeekOrigin.Begin); byte[] datesB = new byte[16]; stream.Read(datesB, 0, 16); AppleSingleFileDates dates = Marshal.ByteArrayToStructureBigEndian <AppleSingleFileDates>(datesB); _creationTime = DateHandlers.MacToDateTime(dates.creationDate); _lastWriteTime = DateHandlers.MacToDateTime(dates.modificationDate); break; case AppleSingleEntryID.FileInfo: stream.Seek(entry.offset, SeekOrigin.Begin); byte[] finfo = new byte[entry.length]; stream.Read(finfo, 0, finfo.Length); if (_macintoshHome.SequenceEqual(_header.homeFilesystem)) { AppleSingleMacFileInfo macinfo = Marshal.ByteArrayToStructureBigEndian <AppleSingleMacFileInfo>(finfo); _creationTime = DateHandlers.MacToDateTime(macinfo.creationDate); _lastWriteTime = DateHandlers.MacToDateTime(macinfo.modificationDate); } else if (_proDosHome.SequenceEqual(_header.homeFilesystem)) { AppleSingleProDOSFileInfo prodosinfo = Marshal.ByteArrayToStructureBigEndian <AppleSingleProDOSFileInfo>(finfo); _creationTime = DateHandlers.MacToDateTime(prodosinfo.creationDate); _lastWriteTime = DateHandlers.MacToDateTime(prodosinfo.modificationDate); } else if (_unixHome.SequenceEqual(_header.homeFilesystem)) { AppleSingleUnixFileInfo unixinfo = Marshal.ByteArrayToStructureBigEndian <AppleSingleUnixFileInfo>(finfo); _creationTime = DateHandlers.UnixUnsignedToDateTime(unixinfo.creationDate); _lastWriteTime = DateHandlers.UnixUnsignedToDateTime(unixinfo.modificationDate); } else if (_dosHome.SequenceEqual(_header.homeFilesystem)) { AppleSingleDOSFileInfo dosinfo = Marshal.ByteArrayToStructureBigEndian <AppleSingleDOSFileInfo>(finfo); _lastWriteTime = DateHandlers.DosToDateTime(dosinfo.modificationDate, dosinfo.modificationTime); } break; case AppleSingleEntryID.ResourceFork: _rsrcFork = entry; break; } } stream.Seek(0, SeekOrigin.Begin); _opened = true; _isStream = true; _stream = stream; }
public void Open(string path) { string filename = Path.GetFileName(path); string filenameNoExt = Path.GetFileNameWithoutExtension(path); string parentFolder = Path.GetDirectoryName(path); parentFolder ??= ""; if (filename is null || filenameNoExt is null) { throw new ArgumentNullException(nameof(path)); } // Prepend data fork name with "R." string proDosAppleDouble = Path.Combine(parentFolder, "R." + filename); // Prepend data fork name with '%' string unixAppleDouble = Path.Combine(parentFolder, "%" + filename); // Change file extension to ADF string dosAppleDouble = Path.Combine(parentFolder, filenameNoExt + ".ADF"); // Change file extension to adf string dosAppleDoubleLower = Path.Combine(parentFolder, filenameNoExt + ".adf"); // Store AppleDouble header file in ".AppleDouble" folder with same name string netatalkAppleDouble = Path.Combine(parentFolder, ".AppleDouble", filename); // Store AppleDouble header file in "resource.frk" folder with same name string daveAppleDouble = Path.Combine(parentFolder, "resource.frk", filename); // Prepend data fork name with "._" string osxAppleDouble = Path.Combine(parentFolder, "._" + filename); // Adds ".rsrc" extension string unArAppleDouble = Path.Combine(parentFolder, filename + ".rsrc"); // Check AppleDouble created by A/UX in ProDOS filesystem if (File.Exists(proDosAppleDouble)) { var prodosStream = new FileStream(proDosAppleDouble, FileMode.Open, FileAccess.Read); if (prodosStream.Length > 26) { byte[] prodosB = new byte[26]; prodosStream.Read(prodosB, 0, 26); _header = Marshal.ByteArrayToStructureBigEndian <Header>(prodosB); prodosStream.Close(); if (_header.magic == MAGIC && (_header.version == VERSION || _header.version == VERSION2)) { _headerPath = proDosAppleDouble; } } } // Check AppleDouble created by A/UX in UFS filesystem if (File.Exists(unixAppleDouble)) { var unixStream = new FileStream(unixAppleDouble, FileMode.Open, FileAccess.Read); if (unixStream.Length > 26) { byte[] unixB = new byte[26]; unixStream.Read(unixB, 0, 26); _header = Marshal.ByteArrayToStructureBigEndian <Header>(unixB); unixStream.Close(); if (_header.magic == MAGIC && (_header.version == VERSION || _header.version == VERSION2)) { _headerPath = unixAppleDouble; } } } // Check AppleDouble created by A/UX in FAT filesystem if (File.Exists(dosAppleDouble)) { var dosStream = new FileStream(dosAppleDouble, FileMode.Open, FileAccess.Read); if (dosStream.Length > 26) { byte[] dosB = new byte[26]; dosStream.Read(dosB, 0, 26); _header = Marshal.ByteArrayToStructureBigEndian <Header>(dosB); dosStream.Close(); if (_header.magic == MAGIC && (_header.version == VERSION || _header.version == VERSION2)) { _headerPath = dosAppleDouble; } } } // Check AppleDouble created by A/UX in case preserving FAT filesystem if (File.Exists(dosAppleDoubleLower)) { var doslStream = new FileStream(dosAppleDoubleLower, FileMode.Open, FileAccess.Read); if (doslStream.Length > 26) { byte[] doslB = new byte[26]; doslStream.Read(doslB, 0, 26); _header = Marshal.ByteArrayToStructureBigEndian <Header>(doslB); doslStream.Close(); if (_header.magic == MAGIC && (_header.version == VERSION || _header.version == VERSION2)) { _headerPath = dosAppleDoubleLower; } } } // Check AppleDouble created by Netatalk if (File.Exists(netatalkAppleDouble)) { var netatalkStream = new FileStream(netatalkAppleDouble, FileMode.Open, FileAccess.Read); if (netatalkStream.Length > 26) { byte[] netatalkB = new byte[26]; netatalkStream.Read(netatalkB, 0, 26); _header = Marshal.ByteArrayToStructureBigEndian <Header>(netatalkB); netatalkStream.Close(); if (_header.magic == MAGIC && (_header.version == VERSION || _header.version == VERSION2)) { _headerPath = netatalkAppleDouble; } } } // Check AppleDouble created by DAVE if (File.Exists(daveAppleDouble)) { var daveStream = new FileStream(daveAppleDouble, FileMode.Open, FileAccess.Read); if (daveStream.Length > 26) { byte[] daveB = new byte[26]; daveStream.Read(daveB, 0, 26); _header = Marshal.ByteArrayToStructureBigEndian <Header>(daveB); daveStream.Close(); if (_header.magic == MAGIC && (_header.version == VERSION || _header.version == VERSION2)) { _headerPath = daveAppleDouble; } } } // Check AppleDouble created by Mac OS X if (File.Exists(osxAppleDouble)) { var osxStream = new FileStream(osxAppleDouble, FileMode.Open, FileAccess.Read); if (osxStream.Length > 26) { byte[] osxB = new byte[26]; osxStream.Read(osxB, 0, 26); _header = Marshal.ByteArrayToStructureBigEndian <Header>(osxB); osxStream.Close(); if (_header.magic == MAGIC && (_header.version == VERSION || _header.version == VERSION2)) { _headerPath = osxAppleDouble; } } } // Check AppleDouble created by UnAr (from The Unarchiver) if (File.Exists(unArAppleDouble)) { var unarStream = new FileStream(unArAppleDouble, FileMode.Open, FileAccess.Read); if (unarStream.Length > 26) { byte[] unarB = new byte[26]; unarStream.Read(unarB, 0, 26); _header = Marshal.ByteArrayToStructureBigEndian <Header>(unarB); unarStream.Close(); if (_header.magic == MAGIC && (_header.version == VERSION || _header.version == VERSION2)) { _headerPath = unArAppleDouble; } } } var fs = new FileStream(_headerPath, FileMode.Open, FileAccess.Read); fs.Seek(0, SeekOrigin.Begin); byte[] hdrB = new byte[26]; fs.Read(hdrB, 0, 26); _header = Marshal.ByteArrayToStructureBigEndian <Header>(hdrB); Entry[] entries = new Entry[_header.entries]; for (int i = 0; i < _header.entries; i++) { byte[] entry = new byte[12]; fs.Read(entry, 0, 12); entries[i] = Marshal.ByteArrayToStructureBigEndian <Entry>(entry); } _creationTime = DateTime.UtcNow; _lastWriteTime = _creationTime; foreach (Entry entry in entries) { switch ((EntryId)entry.id) { case EntryId.DataFork: // AppleDouble have datafork in separated file break; case EntryId.FileDates: fs.Seek(entry.offset, SeekOrigin.Begin); byte[] datesB = new byte[16]; fs.Read(datesB, 0, 16); FileDates dates = Marshal.ByteArrayToStructureBigEndian <FileDates>(datesB); _creationTime = DateHandlers.UnixUnsignedToDateTime(dates.creationDate); _lastWriteTime = DateHandlers.UnixUnsignedToDateTime(dates.modificationDate); break; case EntryId.FileInfo: fs.Seek(entry.offset, SeekOrigin.Begin); byte[] finfo = new byte[entry.length]; fs.Read(finfo, 0, finfo.Length); if (_macintoshHome.SequenceEqual(_header.homeFilesystem)) { MacFileInfo macinfo = Marshal.ByteArrayToStructureBigEndian <MacFileInfo>(finfo); _creationTime = DateHandlers.MacToDateTime(macinfo.creationDate); _lastWriteTime = DateHandlers.MacToDateTime(macinfo.modificationDate); } else if (_proDosHome.SequenceEqual(_header.homeFilesystem)) { ProDOSFileInfo prodosinfo = Marshal.ByteArrayToStructureBigEndian <ProDOSFileInfo>(finfo); _creationTime = DateHandlers.MacToDateTime(prodosinfo.creationDate); _lastWriteTime = DateHandlers.MacToDateTime(prodosinfo.modificationDate); } else if (_unixHome.SequenceEqual(_header.homeFilesystem)) { UnixFileInfo unixinfo = Marshal.ByteArrayToStructureBigEndian <UnixFileInfo>(finfo); _creationTime = DateHandlers.UnixUnsignedToDateTime(unixinfo.creationDate); _lastWriteTime = DateHandlers.UnixUnsignedToDateTime(unixinfo.modificationDate); } else if (_dosHome.SequenceEqual(_header.homeFilesystem)) { DOSFileInfo dosinfo = Marshal.ByteArrayToStructureBigEndian <DOSFileInfo>(finfo); _lastWriteTime = DateHandlers.DosToDateTime(dosinfo.modificationDate, dosinfo.modificationTime); } break; case EntryId.ResourceFork: _rsrcFork = entry; break; } } _dataFork = new Entry { id = (uint)EntryId.DataFork }; if (File.Exists(path)) { var dataFs = new FileStream(path, FileMode.Open, FileAccess.Read); _dataFork.length = (uint)dataFs.Length; dataFs.Close(); } fs.Close(); _opened = true; _basePath = path; }