public bool Open(IFilter imageFilter) { Stream stream = imageFilter.GetDataForkStream(); stream.Seek(0, SeekOrigin.Begin); if (stream.Length < 512) { return(false); } byte[] qHdrB = new byte[48]; stream.Read(qHdrB, 0, 48); qHdr = Marshal.SpanToStructureBigEndian <QCowHeader>(qHdrB); DicConsole.DebugWriteLine("QCOW plugin", "qHdr.magic = 0x{0:X8}", qHdr.magic); DicConsole.DebugWriteLine("QCOW plugin", "qHdr.version = {0}", qHdr.version); DicConsole.DebugWriteLine("QCOW plugin", "qHdr.backing_file_offset = {0}", qHdr.backing_file_offset); DicConsole.DebugWriteLine("QCOW plugin", "qHdr.backing_file_size = {0}", qHdr.backing_file_size); DicConsole.DebugWriteLine("QCOW plugin", "qHdr.mtime = {0}", qHdr.mtime); DicConsole.DebugWriteLine("QCOW plugin", "qHdr.size = {0}", qHdr.size); DicConsole.DebugWriteLine("QCOW plugin", "qHdr.cluster_bits = {0}", qHdr.cluster_bits); DicConsole.DebugWriteLine("QCOW plugin", "qHdr.l2_bits = {0}", qHdr.l2_bits); DicConsole.DebugWriteLine("QCOW plugin", "qHdr.padding = {0}", qHdr.padding); DicConsole.DebugWriteLine("QCOW plugin", "qHdr.crypt_method = {0}", qHdr.crypt_method); DicConsole.DebugWriteLine("QCOW plugin", "qHdr.l1_table_offset = {0}", qHdr.l1_table_offset); if (qHdr.size <= 1) { throw new ArgumentOutOfRangeException(nameof(qHdr.size), "Image size is too small"); } if (qHdr.cluster_bits < 9 || qHdr.cluster_bits > 16) { throw new ArgumentOutOfRangeException(nameof(qHdr.cluster_bits), "Cluster size must be between 512 bytes and 64 Kbytes"); } if (qHdr.l2_bits < 9 - 3 || qHdr.l2_bits > 16 - 3) { throw new ArgumentOutOfRangeException(nameof(qHdr.l2_bits), "L2 size must be between 512 bytes and 64 Kbytes"); } if (qHdr.crypt_method > QCOW_ENCRYPTION_AES) { throw new ArgumentOutOfRangeException(nameof(qHdr.crypt_method), "Invalid encryption method"); } if (qHdr.crypt_method > QCOW_ENCRYPTION_NONE) { throw new NotImplementedException("AES encrypted images not yet supported"); } if (qHdr.backing_file_offset != 0) { throw new NotImplementedException("Differencing images not yet supported"); } int shift = qHdr.cluster_bits + qHdr.l2_bits; if (qHdr.size > ulong.MaxValue - (ulong)(1 << shift)) { throw new ArgumentOutOfRangeException(nameof(qHdr.size), "Image is too large"); } clusterSize = 1 << qHdr.cluster_bits; clusterSectors = 1 << (qHdr.cluster_bits - 9); l1Size = (uint)((qHdr.size + (ulong)(1 << shift) - 1) >> shift); l2Size = 1 << qHdr.l2_bits; DicConsole.DebugWriteLine("QCOW plugin", "qHdr.clusterSize = {0}", clusterSize); DicConsole.DebugWriteLine("QCOW plugin", "qHdr.clusterSectors = {0}", clusterSectors); DicConsole.DebugWriteLine("QCOW plugin", "qHdr.l1Size = {0}", l1Size); DicConsole.DebugWriteLine("QCOW plugin", "qHdr.l2Size = {0}", l2Size); DicConsole.DebugWriteLine("QCOW plugin", "qHdr.sectors = {0}", imageInfo.Sectors); BigEndianBitConverter.IsLittleEndian = BitConverter.IsLittleEndian; byte[] l1TableB = new byte[l1Size * 8]; stream.Seek((long)qHdr.l1_table_offset, SeekOrigin.Begin); stream.Read(l1TableB, 0, (int)l1Size * 8); l1Table = MemoryMarshal.Cast <byte, ulong>(l1TableB).ToArray(); DicConsole.DebugWriteLine("QCOW plugin", "Reading L1 table"); for (long i = 0; i < l1Table.LongLength; i++) { l1Table[i] = Swapping.Swap(l1Table[i]); } l1Mask = 0; int c = 0; l1Shift = qHdr.l2_bits + qHdr.cluster_bits; for (int i = 0; i < 64; i++) { l1Mask <<= 1; if (c >= 64 - l1Shift) { continue; } l1Mask += 1; c++; } l2Mask = 0; for (int i = 0; i < qHdr.l2_bits; i++) { l2Mask = (l2Mask << 1) + 1; } l2Mask <<= qHdr.cluster_bits; sectorMask = 0; for (int i = 0; i < qHdr.cluster_bits; i++) { sectorMask = (sectorMask << 1) + 1; } DicConsole.DebugWriteLine("QCOW plugin", "qHdr.l1Mask = {0:X}", l1Mask); DicConsole.DebugWriteLine("QCOW plugin", "qHdr.l1Shift = {0}", l1Shift); DicConsole.DebugWriteLine("QCOW plugin", "qHdr.l2Mask = {0:X}", l2Mask); DicConsole.DebugWriteLine("QCOW plugin", "qHdr.sectorMask = {0:X}", sectorMask); maxL2TableCache = MAX_CACHE_SIZE / (l2Size * 8); maxClusterCache = MAX_CACHE_SIZE / clusterSize; imageStream = stream; sectorCache = new Dictionary <ulong, byte[]>(); l2TableCache = new Dictionary <ulong, ulong[]>(); clusterCache = new Dictionary <ulong, byte[]>(); imageInfo.CreationTime = imageFilter.GetCreationTime(); imageInfo.LastModificationTime = qHdr.mtime > 0 ? DateHandlers.UnixUnsignedToDateTime(qHdr.mtime) : imageFilter.GetLastWriteTime(); imageInfo.MediaTitle = Path.GetFileNameWithoutExtension(imageFilter.GetFilename()); imageInfo.Sectors = qHdr.size / 512; imageInfo.SectorSize = 512; imageInfo.XmlMediaType = XmlMediaType.BlockMedia; imageInfo.MediaType = MediaType.GENERIC_HDD; imageInfo.ImageSize = qHdr.size; imageInfo.Cylinders = (uint)(imageInfo.Sectors / 16 / 63); imageInfo.Heads = 16; imageInfo.SectorsPerTrack = 63; return(true); }
public bool Open(IFilter imageFilter) { Stream imageStream = imageFilter.GetDataForkStream(); byte[] header = new byte[512]; byte[] footer; imageStream.Seek(0, SeekOrigin.Begin); imageStream.Read(header, 0, 512); if (imageStream.Length % 2 == 0) { footer = new byte[512]; imageStream.Seek(-512, SeekOrigin.End); imageStream.Read(footer, 0, 512); } else { footer = new byte[511]; imageStream.Seek(-511, SeekOrigin.End); imageStream.Read(footer, 0, 511); } uint headerChecksum = BigEndianBitConverter.ToUInt32(header, 0x40); uint footerChecksum = BigEndianBitConverter.ToUInt32(footer, 0x40); ulong headerCookie = BigEndianBitConverter.ToUInt64(header, 0); ulong footerCookie = BigEndianBitConverter.ToUInt64(footer, 0); header[0x40] = 0; header[0x41] = 0; header[0x42] = 0; header[0x43] = 0; footer[0x40] = 0; footer[0x41] = 0; footer[0x42] = 0; footer[0x43] = 0; uint headerCalculatedChecksum = VhdChecksum(header); uint footerCalculatedChecksum = VhdChecksum(footer); AaruConsole.DebugWriteLine("VirtualPC plugin", "Header checksum = 0x{0:X8}, calculated = 0x{1:X8}", headerChecksum, headerCalculatedChecksum); AaruConsole.DebugWriteLine("VirtualPC plugin", "Header checksum = 0x{0:X8}, calculated = 0x{1:X8}", footerChecksum, footerCalculatedChecksum); byte[] usableHeader; uint usableChecksum; if (headerCookie == IMAGE_COOKIE && headerChecksum == headerCalculatedChecksum) { usableHeader = header; usableChecksum = headerChecksum; } else if (footerCookie == IMAGE_COOKIE && footerChecksum == footerCalculatedChecksum) { usableHeader = footer; usableChecksum = footerChecksum; } else { throw new ImageNotSupportedException("(VirtualPC plugin): Both header and footer are corrupt, image cannot be opened."); } _thisFooter = new HardDiskFooter { Cookie = BigEndianBitConverter.ToUInt64(usableHeader, 0x00), Features = BigEndianBitConverter.ToUInt32(usableHeader, 0x08), Version = BigEndianBitConverter.ToUInt32(usableHeader, 0x0C), Offset = BigEndianBitConverter.ToUInt64(usableHeader, 0x10), Timestamp = BigEndianBitConverter.ToUInt32(usableHeader, 0x18), CreatorApplication = BigEndianBitConverter.ToUInt32(usableHeader, 0x1C), CreatorVersion = BigEndianBitConverter.ToUInt32(usableHeader, 0x20), CreatorHostOs = BigEndianBitConverter.ToUInt32(usableHeader, 0x24), OriginalSize = BigEndianBitConverter.ToUInt64(usableHeader, 0x28), CurrentSize = BigEndianBitConverter.ToUInt64(usableHeader, 0x30), DiskGeometry = BigEndianBitConverter.ToUInt32(usableHeader, 0x38), DiskType = BigEndianBitConverter.ToUInt32(usableHeader, 0x3C), Checksum = usableChecksum, UniqueId = BigEndianBitConverter.ToGuid(usableHeader, 0x44), SavedState = usableHeader[0x54], Reserved = new byte[usableHeader.Length - 0x55] }; Array.Copy(usableHeader, 0x55, _thisFooter.Reserved, 0, usableHeader.Length - 0x55); _thisDateTime = new DateTime(2000, 1, 1, 0, 0, 0, DateTimeKind.Utc); _thisDateTime = _thisDateTime.AddSeconds(_thisFooter.Timestamp); var sha1Ctx = new Sha1Context(); sha1Ctx.Update(_thisFooter.Reserved); AaruConsole.DebugWriteLine("VirtualPC plugin", "footer.cookie = 0x{0:X8}", _thisFooter.Cookie); AaruConsole.DebugWriteLine("VirtualPC plugin", "footer.features = 0x{0:X8}", _thisFooter.Features); AaruConsole.DebugWriteLine("VirtualPC plugin", "footer.version = 0x{0:X8}", _thisFooter.Version); AaruConsole.DebugWriteLine("VirtualPC plugin", "footer.offset = {0}", _thisFooter.Offset); AaruConsole.DebugWriteLine("VirtualPC plugin", "footer.timestamp = 0x{0:X8} ({1})", _thisFooter.Timestamp, _thisDateTime); AaruConsole.DebugWriteLine("VirtualPC plugin", "footer.creatorApplication = 0x{0:X8} (\"{1}\")", _thisFooter.CreatorApplication, Encoding.ASCII.GetString(BigEndianBitConverter.GetBytes(_thisFooter. CreatorApplication))); AaruConsole.DebugWriteLine("VirtualPC plugin", "footer.creatorVersion = 0x{0:X8}", _thisFooter.CreatorVersion); AaruConsole.DebugWriteLine("VirtualPC plugin", "footer.creatorHostOS = 0x{0:X8} (\"{1}\")", _thisFooter.CreatorHostOs, Encoding.ASCII.GetString(BigEndianBitConverter. GetBytes(_thisFooter.CreatorHostOs))); AaruConsole.DebugWriteLine("VirtualPC plugin", "footer.originalSize = {0}", _thisFooter.OriginalSize); AaruConsole.DebugWriteLine("VirtualPC plugin", "footer.currentSize = {0}", _thisFooter.CurrentSize); AaruConsole.DebugWriteLine("VirtualPC plugin", "footer.diskGeometry = 0x{0:X8} (C/H/S: {1}/{2}/{3})", _thisFooter.DiskGeometry, (_thisFooter.DiskGeometry & 0xFFFF0000) >> 16, (_thisFooter.DiskGeometry & 0xFF00) >> 8, _thisFooter.DiskGeometry & 0xFF); AaruConsole.DebugWriteLine("VirtualPC plugin", "footer.diskType = 0x{0:X8}", _thisFooter.DiskType); AaruConsole.DebugWriteLine("VirtualPC plugin", "footer.checksum = 0x{0:X8}", _thisFooter.Checksum); AaruConsole.DebugWriteLine("VirtualPC plugin", "footer.uniqueId = {0}", _thisFooter.UniqueId); AaruConsole.DebugWriteLine("VirtualPC plugin", "footer.savedState = 0x{0:X2}", _thisFooter.SavedState); AaruConsole.DebugWriteLine("VirtualPC plugin", "footer.reserved's SHA1 = 0x{0}", sha1Ctx.End()); if (_thisFooter.Version == VERSION1) { _imageInfo.Version = "1.0"; } else { throw new ImageNotSupportedException($"(VirtualPC plugin): Unknown image type {_thisFooter.DiskType} found. Please submit a bug with an example image."); } switch (_thisFooter.CreatorApplication) { case CREATOR_QEMU: { _imageInfo.Application = "QEMU"; // QEMU always set same version _imageInfo.ApplicationVersion = "Unknown"; break; } case CREATOR_VIRTUAL_BOX: { _imageInfo.ApplicationVersion = $"{(_thisFooter.CreatorVersion & 0xFFFF0000) >> 16}.{_thisFooter.CreatorVersion & 0x0000FFFF:D2}"; switch (_thisFooter.CreatorHostOs) { case CREATOR_MACINTOSH: case CREATOR_MACINTOSH_OLD: _imageInfo.Application = "VirtualBox for Mac"; break; case CREATOR_WINDOWS: // VirtualBox uses Windows creator for any other OS _imageInfo.Application = "VirtualBox"; break; default: _imageInfo.Application = $"VirtualBox for unknown OS \"{Encoding.ASCII.GetString(BigEndianBitConverter.GetBytes(_thisFooter.CreatorHostOs))}\""; break; } break; } case CREATOR_VIRTUAL_SERVER: { _imageInfo.Application = "Microsoft Virtual Server"; switch (_thisFooter.CreatorVersion) { case VERSION_VIRTUAL_SERVER2004: _imageInfo.ApplicationVersion = "2004"; break; default: _imageInfo.ApplicationVersion = $"Unknown version 0x{_thisFooter.CreatorVersion:X8}"; break; } break; } case CREATOR_VIRTUAL_PC: { switch (_thisFooter.CreatorHostOs) { case CREATOR_MACINTOSH: case CREATOR_MACINTOSH_OLD: switch (_thisFooter.CreatorVersion) { case VERSION_VIRTUAL_PC_MAC: _imageInfo.Application = "Connectix Virtual PC"; _imageInfo.ApplicationVersion = "5, 6 or 7"; break; default: _imageInfo.ApplicationVersion = $"Unknown version 0x{_thisFooter.CreatorVersion:X8}"; break; } break; case CREATOR_WINDOWS: switch (_thisFooter.CreatorVersion) { case VERSION_VIRTUAL_PC_MAC: _imageInfo.Application = "Connectix Virtual PC"; _imageInfo.ApplicationVersion = "5, 6 or 7"; break; case VERSION_VIRTUAL_PC2004: _imageInfo.Application = "Microsoft Virtual PC"; _imageInfo.ApplicationVersion = "2004"; break; case VERSION_VIRTUAL_PC2007: _imageInfo.Application = "Microsoft Virtual PC"; _imageInfo.ApplicationVersion = "2007"; break; default: _imageInfo.ApplicationVersion = $"Unknown version 0x{_thisFooter.CreatorVersion:X8}"; break; } break; default: _imageInfo.Application = $"Virtual PC for unknown OS \"{Encoding.ASCII.GetString(BigEndianBitConverter.GetBytes(_thisFooter.CreatorHostOs))}\""; _imageInfo.ApplicationVersion = $"Unknown version 0x{_thisFooter.CreatorVersion:X8}"; break; } break; } case CREATOR_DISCIMAGECHEF: { _imageInfo.Application = "DiscImageChef"; _imageInfo.ApplicationVersion = $"{(_thisFooter.CreatorVersion & 0xFF000000) >> 24}.{(_thisFooter.CreatorVersion & 0xFF0000) >> 16}.{(_thisFooter.CreatorVersion & 0xFF00) >> 8}.{_thisFooter.CreatorVersion & 0xFF}"; } break; case CREATOR_AARU: { _imageInfo.Application = "Aaru"; _imageInfo.ApplicationVersion = $"{(_thisFooter.CreatorVersion & 0xFF000000) >> 24}.{(_thisFooter.CreatorVersion & 0xFF0000) >> 16}.{(_thisFooter.CreatorVersion & 0xFF00) >> 8}.{_thisFooter.CreatorVersion & 0xFF}"; } break; default: { _imageInfo.Application = $"Unknown application \"{Encoding.ASCII.GetString(BigEndianBitConverter.GetBytes(_thisFooter.CreatorHostOs))}\""; _imageInfo.ApplicationVersion = $"Unknown version 0x{_thisFooter.CreatorVersion:X8}"; break; } } _thisFilter = imageFilter; _imageInfo.ImageSize = _thisFooter.CurrentSize; _imageInfo.Sectors = _thisFooter.CurrentSize / 512; _imageInfo.SectorSize = 512; _imageInfo.CreationTime = imageFilter.GetCreationTime(); _imageInfo.LastModificationTime = _thisDateTime; _imageInfo.MediaTitle = Path.GetFileNameWithoutExtension(imageFilter.GetFilename()); _imageInfo.Cylinders = (_thisFooter.DiskGeometry & 0xFFFF0000) >> 16; _imageInfo.Heads = (_thisFooter.DiskGeometry & 0xFF00) >> 8; _imageInfo.SectorsPerTrack = _thisFooter.DiskGeometry & 0xFF; if (_thisFooter.DiskType == TYPE_DYNAMIC || _thisFooter.DiskType == TYPE_DIFFERENCING) { imageStream.Seek((long)_thisFooter.Offset, SeekOrigin.Begin); byte[] dynamicBytes = new byte[1024]; imageStream.Read(dynamicBytes, 0, 1024); uint dynamicChecksum = BigEndianBitConverter.ToUInt32(dynamicBytes, 0x24); dynamicBytes[0x24] = 0; dynamicBytes[0x25] = 0; dynamicBytes[0x26] = 0; dynamicBytes[0x27] = 0; uint dynamicChecksumCalculated = VhdChecksum(dynamicBytes); AaruConsole.DebugWriteLine("VirtualPC plugin", "Dynamic header checksum = 0x{0:X8}, calculated = 0x{1:X8}", dynamicChecksum, dynamicChecksumCalculated); if (dynamicChecksum != dynamicChecksumCalculated) { throw new ImageNotSupportedException("(VirtualPC plugin): Both header and footer are corrupt, image cannot be opened."); } _thisDynamic = new DynamicDiskHeader { LocatorEntries = new ParentLocatorEntry[8], Reserved2 = new byte[256] }; for (int i = 0; i < 8; i++) { _thisDynamic.LocatorEntries[i] = new ParentLocatorEntry(); } _thisDynamic.Cookie = BigEndianBitConverter.ToUInt64(dynamicBytes, 0x00); _thisDynamic.DataOffset = BigEndianBitConverter.ToUInt64(dynamicBytes, 0x08); _thisDynamic.TableOffset = BigEndianBitConverter.ToUInt64(dynamicBytes, 0x10); _thisDynamic.HeaderVersion = BigEndianBitConverter.ToUInt32(dynamicBytes, 0x18); _thisDynamic.MaxTableEntries = BigEndianBitConverter.ToUInt32(dynamicBytes, 0x1C); _thisDynamic.BlockSize = BigEndianBitConverter.ToUInt32(dynamicBytes, 0x20); _thisDynamic.Checksum = dynamicChecksum; _thisDynamic.ParentId = BigEndianBitConverter.ToGuid(dynamicBytes, 0x28); _thisDynamic.ParentTimestamp = BigEndianBitConverter.ToUInt32(dynamicBytes, 0x38); _thisDynamic.Reserved = BigEndianBitConverter.ToUInt32(dynamicBytes, 0x3C); _thisDynamic.ParentName = Encoding.BigEndianUnicode.GetString(dynamicBytes, 0x40, 512); for (int i = 0; i < 8; i++) { _thisDynamic.LocatorEntries[i].PlatformCode = BigEndianBitConverter.ToUInt32(dynamicBytes, 0x240 + 0x00 + (24 * i)); _thisDynamic.LocatorEntries[i].PlatformDataSpace = BigEndianBitConverter.ToUInt32(dynamicBytes, 0x240 + 0x04 + (24 * i)); _thisDynamic.LocatorEntries[i].PlatformDataLength = BigEndianBitConverter.ToUInt32(dynamicBytes, 0x240 + 0x08 + (24 * i)); _thisDynamic.LocatorEntries[i].Reserved = BigEndianBitConverter.ToUInt32(dynamicBytes, 0x240 + 0x0C + (24 * i)); _thisDynamic.LocatorEntries[i].PlatformDataOffset = BigEndianBitConverter.ToUInt64(dynamicBytes, 0x240 + 0x10 + (24 * i)); } Array.Copy(dynamicBytes, 0x300, _thisDynamic.Reserved2, 0, 256); _parentDateTime = new DateTime(2000, 1, 1, 0, 0, 0, DateTimeKind.Utc); _parentDateTime = _parentDateTime.AddSeconds(_thisDynamic.ParentTimestamp); sha1Ctx = new Sha1Context(); sha1Ctx.Update(_thisDynamic.Reserved2); AaruConsole.DebugWriteLine("VirtualPC plugin", "dynamic.cookie = 0x{0:X8}", _thisDynamic.Cookie); AaruConsole.DebugWriteLine("VirtualPC plugin", "dynamic.dataOffset = {0}", _thisDynamic.DataOffset); AaruConsole.DebugWriteLine("VirtualPC plugin", "dynamic.tableOffset = {0}", _thisDynamic.TableOffset); AaruConsole.DebugWriteLine("VirtualPC plugin", "dynamic.headerVersion = 0x{0:X8}", _thisDynamic.HeaderVersion); AaruConsole.DebugWriteLine("VirtualPC plugin", "dynamic.maxTableEntries = {0}", _thisDynamic.MaxTableEntries); AaruConsole.DebugWriteLine("VirtualPC plugin", "dynamic.blockSize = {0}", _thisDynamic.BlockSize); AaruConsole.DebugWriteLine("VirtualPC plugin", "dynamic.checksum = 0x{0:X8}", _thisDynamic.Checksum); AaruConsole.DebugWriteLine("VirtualPC plugin", "dynamic.parentID = {0}", _thisDynamic.ParentId); AaruConsole.DebugWriteLine("VirtualPC plugin", "dynamic.parentTimestamp = 0x{0:X8} ({1})", _thisDynamic.ParentTimestamp, _parentDateTime); AaruConsole.DebugWriteLine("VirtualPC plugin", "dynamic.reserved = 0x{0:X8}", _thisDynamic.Reserved); for (int i = 0; i < 8; i++) { AaruConsole.DebugWriteLine("VirtualPC plugin", "dynamic.locatorEntries[{0}].platformCode = 0x{1:X8} (\"{2}\")", i, _thisDynamic.LocatorEntries[i].PlatformCode, Encoding.ASCII.GetString(BigEndianBitConverter.GetBytes(_thisDynamic. LocatorEntries[i].PlatformCode))); AaruConsole.DebugWriteLine("VirtualPC plugin", "dynamic.locatorEntries[{0}].platformDataSpace = {1}", i, _thisDynamic.LocatorEntries[i].PlatformDataSpace); AaruConsole.DebugWriteLine("VirtualPC plugin", "dynamic.locatorEntries[{0}].platformDataLength = {1}", i, _thisDynamic.LocatorEntries[i].PlatformDataLength); AaruConsole.DebugWriteLine("VirtualPC plugin", "dynamic.locatorEntries[{0}].reserved = 0x{1:X8}", i, _thisDynamic.LocatorEntries[i].Reserved); AaruConsole.DebugWriteLine("VirtualPC plugin", "dynamic.locatorEntries[{0}].platformDataOffset = {1}", i, _thisDynamic.LocatorEntries[i].PlatformDataOffset); } AaruConsole.DebugWriteLine("VirtualPC plugin", "dynamic.parentName = \"{0}\"", _thisDynamic.ParentName); AaruConsole.DebugWriteLine("VirtualPC plugin", "dynamic.reserved2's SHA1 = 0x{0}", sha1Ctx.End()); if (_thisDynamic.HeaderVersion != VERSION1) { throw new ImageNotSupportedException($"(VirtualPC plugin): Unknown image type {_thisFooter.DiskType} found. Please submit a bug with an example image."); } DateTime startTime = DateTime.UtcNow; _blockAllocationTable = new uint[_thisDynamic.MaxTableEntries]; // How many sectors uses the BAT int batSectorCount = (int)Math.Ceiling(((double)_thisDynamic.MaxTableEntries * 4) / 512); byte[] bat = new byte[_thisDynamic.MaxTableEntries * 4]; imageStream.Seek((long)_thisDynamic.TableOffset, SeekOrigin.Begin); imageStream.Read(bat, 0, batSectorCount); ReadOnlySpan <byte> span = bat; _blockAllocationTable = MemoryMarshal.Cast <byte, uint>(span). Slice(0, (int)_thisDynamic.MaxTableEntries).ToArray(); for (int i = 0; i < _blockAllocationTable.Length; i++) { _blockAllocationTable[i] = Swapping.Swap(_blockAllocationTable[i]); } DateTime endTime = DateTime.UtcNow; AaruConsole.DebugWriteLine("VirtualPC plugin", "Filling the BAT took {0} seconds", (endTime - startTime).TotalSeconds); _bitmapSize = (uint)Math.Ceiling((double)_thisDynamic.BlockSize / 512 // 1 bit per sector on the bitmap / 8 // and aligned to 512 byte boundary / 512); AaruConsole.DebugWriteLine("VirtualPC plugin", "Bitmap is {0} sectors", _bitmapSize); } _imageInfo.XmlMediaType = XmlMediaType.BlockMedia; switch (_thisFooter.DiskType) { case TYPE_FIXED: case TYPE_DYNAMIC: { // Nothing to do here, really. return(true); } case TYPE_DIFFERENCING: { _locatorEntriesData = new byte[8][]; for (int i = 0; i < 8; i++) { if (_thisDynamic.LocatorEntries[i].PlatformCode != 0x00000000) { _locatorEntriesData[i] = new byte[_thisDynamic.LocatorEntries[i].PlatformDataLength]; imageStream.Seek((long)_thisDynamic.LocatorEntries[i].PlatformDataOffset, SeekOrigin.Begin); imageStream.Read(_locatorEntriesData[i], 0, (int)_thisDynamic.LocatorEntries[i].PlatformDataLength); switch (_thisDynamic.LocatorEntries[i].PlatformCode) { case PLATFORM_CODE_WINDOWS_ABSOLUTE: case PLATFORM_CODE_WINDOWS_RELATIVE: AaruConsole.DebugWriteLine("VirtualPC plugin", "dynamic.locatorEntries[{0}] = \"{1}\"", i, Encoding.ASCII.GetString(_locatorEntriesData[i])); break; case PLATFORM_CODE_WINDOWS_ABSOLUTE_U: case PLATFORM_CODE_WINDOWS_RELATIVE_U: AaruConsole.DebugWriteLine("VirtualPC plugin", "dynamic.locatorEntries[{0}] = \"{1}\"", i, Encoding.BigEndianUnicode. GetString(_locatorEntriesData[i])); break; case PLATFORM_CODE_MACINTOSH_URI: AaruConsole.DebugWriteLine("VirtualPC plugin", "dynamic.locatorEntries[{0}] = \"{1}\"", i, Encoding.UTF8.GetString(_locatorEntriesData[i])); break; default: AaruConsole.DebugWriteLine("VirtualPC plugin", "dynamic.locatorEntries[{0}] =", i); PrintHex.PrintHexArray(_locatorEntriesData[i], 64); break; } } } int currentLocator = 0; bool locatorFound = false; string parentPath = null; while (!locatorFound && currentLocator < 8) { switch (_thisDynamic.LocatorEntries[currentLocator].PlatformCode) { case PLATFORM_CODE_WINDOWS_ABSOLUTE: case PLATFORM_CODE_WINDOWS_RELATIVE: parentPath = Encoding.ASCII.GetString(_locatorEntriesData[currentLocator]); break; case PLATFORM_CODE_WINDOWS_ABSOLUTE_U: case PLATFORM_CODE_WINDOWS_RELATIVE_U: parentPath = Encoding.BigEndianUnicode.GetString(_locatorEntriesData[currentLocator]); break; case PLATFORM_CODE_MACINTOSH_URI: parentPath = Uri.UnescapeDataString(Encoding.UTF8. GetString(_locatorEntriesData[currentLocator])); if (parentPath.StartsWith("file://localhost", StringComparison.InvariantCulture)) { parentPath = parentPath.Remove(0, 16); } else { AaruConsole.DebugWriteLine("VirtualPC plugin", "Unsupported protocol classified found in URI parent path: \"{0}\"", parentPath); parentPath = null; } break; } if (parentPath != null) { AaruConsole.DebugWriteLine("VirtualPC plugin", "Possible parent path: \"{0}\"", parentPath); IFilter parentFilter = new FiltersList().GetFilter(Path.Combine(imageFilter.GetParentFolder(), parentPath)); if (parentFilter != null) { locatorFound = true; } if (!locatorFound) { parentPath = null; } } currentLocator++; } if (!locatorFound) { throw new FileNotFoundException("(VirtualPC plugin): Cannot find parent file for differencing disk image"); } { _parentImage = new Vhd(); IFilter parentFilter = new FiltersList().GetFilter(Path.Combine(imageFilter.GetParentFolder(), parentPath)); if (parentFilter == null) { throw new ImageNotSupportedException("(VirtualPC plugin): Cannot find parent image filter"); } /* PluginBase plugins = new PluginBase(); * plugins.RegisterAllPlugins(); * if (!plugins.ImagePluginsList.TryGetValue(Name.ToLower(), out parentImage)) * throw new SystemException("(VirtualPC plugin): Unable to open myself");*/ if (!_parentImage.Identify(parentFilter)) { throw new ImageNotSupportedException("(VirtualPC plugin): Parent image is not a Virtual PC disk image"); } if (!_parentImage.Open(parentFilter)) { throw new ImageNotSupportedException("(VirtualPC plugin): Cannot open parent disk image"); } // While specification says that parent and child disk images should contain UUID relationship // in reality it seems that old differencing disk images stored a parent UUID that, nonetheless // the parent never stored itself. So the only real way to know that images are related is // because the parent IS found and SAME SIZE. Ugly... // More funny even, tested parent images show an empty host OS, and child images a correct one. if (_parentImage.Info.Sectors != _imageInfo.Sectors) { throw new ImageNotSupportedException("(VirtualPC plugin): Parent image is of different size"); } } return(true); } case TYPE_DEPRECATED1: case TYPE_DEPRECATED2: case TYPE_DEPRECATED3: { throw new ImageNotSupportedException("(VirtualPC plugin): Deprecated image type found. Please submit a bug with an example image."); } default: { throw new ImageNotSupportedException($"(VirtualPC plugin): Unknown image type {_thisFooter.DiskType} found. Please submit a bug with an example image."); } } }
public Errno Mount(IMediaImage imagePlugin, Partition partition, Encoding encoding, Dictionary<string, string> options, string @namespace) { Encoding = Encoding.GetEncoding("iso-8859-15"); _littleEndian = true; options ??= GetDefaultOptions(); if(options.TryGetValue("debug", out string debugString)) bool.TryParse(debugString, out _debug); if(imagePlugin.Info.SectorSize < 512) return Errno.InvalidArgument; AaruConsole.DebugWriteLine("Xbox FAT plugin", "Reading superblock"); byte[] sector = imagePlugin.ReadSector(partition.Start); _superblock = Marshal.ByteArrayToStructureLittleEndian<Superblock>(sector); if(_superblock.magic == FATX_CIGAM) { _superblock = Marshal.ByteArrayToStructureBigEndian<Superblock>(sector); _littleEndian = false; } if(_superblock.magic != FATX_MAGIC) return Errno.InvalidArgument; AaruConsole.DebugWriteLine("Xbox FAT plugin", _littleEndian ? "Filesystem is little endian" : "Filesystem is big endian"); int logicalSectorsPerPhysicalSectors = partition.Offset == 0 && _littleEndian ? 8 : 1; AaruConsole.DebugWriteLine("Xbox FAT plugin", "logicalSectorsPerPhysicalSectors = {0}", logicalSectorsPerPhysicalSectors); string volumeLabel = StringHandlers.CToString(_superblock.volumeLabel, !_littleEndian ? Encoding.BigEndianUnicode : Encoding.Unicode, true); XmlFsType = new FileSystemType { Type = "FATX filesystem", ClusterSize = (uint)(_superblock.sectorsPerCluster * logicalSectorsPerPhysicalSectors * imagePlugin.Info.SectorSize), VolumeName = volumeLabel, VolumeSerial = $"{_superblock.id:X8}" }; XmlFsType.Clusters = (((partition.End - partition.Start) + 1) * imagePlugin.Info.SectorSize) / XmlFsType.ClusterSize; _statfs = new FileSystemInfo { Blocks = XmlFsType.Clusters, FilenameLength = MAX_FILENAME, Files = 0, // Requires traversing all directories FreeFiles = 0, Id = { IsInt = true, Serial32 = _superblock.magic }, PluginId = Id, Type = _littleEndian ? "Xbox FAT" : "Xbox 360 FAT", FreeBlocks = 0 // Requires traversing the FAT }; AaruConsole.DebugWriteLine("Xbox FAT plugin", "XmlFsType.ClusterSize: {0}", XmlFsType.ClusterSize); AaruConsole.DebugWriteLine("Xbox FAT plugin", "XmlFsType.VolumeName: {0}", XmlFsType.VolumeName); AaruConsole.DebugWriteLine("Xbox FAT plugin", "XmlFsType.VolumeSerial: {0}", XmlFsType.VolumeSerial); AaruConsole.DebugWriteLine("Xbox FAT plugin", "stat.Blocks: {0}", _statfs.Blocks); AaruConsole.DebugWriteLine("Xbox FAT plugin", "stat.FilenameLength: {0}", _statfs.FilenameLength); AaruConsole.DebugWriteLine("Xbox FAT plugin", "stat.Id: {0}", _statfs.Id.Serial32); AaruConsole.DebugWriteLine("Xbox FAT plugin", "stat.Type: {0}", _statfs.Type); byte[] buffer; _fatStartSector = (FAT_START / imagePlugin.Info.SectorSize) + partition.Start; uint fatSize; AaruConsole.DebugWriteLine("Xbox FAT plugin", "fatStartSector: {0}", _fatStartSector); if(_statfs.Blocks > MAX_XFAT16_CLUSTERS) { AaruConsole.DebugWriteLine("Xbox FAT plugin", "Reading FAT32"); fatSize = (uint)(((_statfs.Blocks + 1) * sizeof(uint)) / imagePlugin.Info.SectorSize); if((uint)(((_statfs.Blocks + 1) * sizeof(uint)) % imagePlugin.Info.SectorSize) > 0) fatSize++; long fatClusters = (fatSize * imagePlugin.Info.SectorSize) / 4096; if((fatSize * imagePlugin.Info.SectorSize) % 4096 > 0) fatClusters++; fatSize = (uint)((fatClusters * 4096) / imagePlugin.Info.SectorSize); AaruConsole.DebugWriteLine("Xbox FAT plugin", "FAT is {0} sectors", fatSize); buffer = imagePlugin.ReadSectors(_fatStartSector, fatSize); AaruConsole.DebugWriteLine("Xbox FAT plugin", "Casting FAT"); _fat32 = MemoryMarshal.Cast<byte, uint>(buffer).ToArray(); if(!_littleEndian) for(int i = 0; i < _fat32.Length; i++) _fat32[i] = Swapping.Swap(_fat32[i]); AaruConsole.DebugWriteLine("Xbox FAT plugin", "fat32[0] == FATX32_ID = {0}", _fat32[0] == FATX32_ID); if(_fat32[0] != FATX32_ID) return Errno.InvalidArgument; } else { AaruConsole.DebugWriteLine("Xbox FAT plugin", "Reading FAT16"); fatSize = (uint)(((_statfs.Blocks + 1) * sizeof(ushort)) / imagePlugin.Info.SectorSize); if((uint)(((_statfs.Blocks + 1) * sizeof(ushort)) % imagePlugin.Info.SectorSize) > 0) fatSize++; long fatClusters = (fatSize * imagePlugin.Info.SectorSize) / 4096; if((fatSize * imagePlugin.Info.SectorSize) % 4096 > 0) fatClusters++; fatSize = (uint)((fatClusters * 4096) / imagePlugin.Info.SectorSize); AaruConsole.DebugWriteLine("Xbox FAT plugin", "FAT is {0} sectors", fatSize); buffer = imagePlugin.ReadSectors(_fatStartSector, fatSize); AaruConsole.DebugWriteLine("Xbox FAT plugin", "Casting FAT"); _fat16 = MemoryMarshal.Cast<byte, ushort>(buffer).ToArray(); if(!_littleEndian) for(int i = 0; i < _fat16.Length; i++) _fat16[i] = Swapping.Swap(_fat16[i]); AaruConsole.DebugWriteLine("Xbox FAT plugin", "fat16[0] == FATX16_ID = {0}", _fat16[0] == FATX16_ID); if(_fat16[0] != FATX16_ID) return Errno.InvalidArgument; } _sectorsPerCluster = (uint)(_superblock.sectorsPerCluster * logicalSectorsPerPhysicalSectors); _imagePlugin = imagePlugin; _firstClusterSector = _fatStartSector + fatSize; _bytesPerCluster = _sectorsPerCluster * imagePlugin.Info.SectorSize; AaruConsole.DebugWriteLine("Xbox FAT plugin", "sectorsPerCluster = {0}", _sectorsPerCluster); AaruConsole.DebugWriteLine("Xbox FAT plugin", "bytesPerCluster = {0}", _bytesPerCluster); AaruConsole.DebugWriteLine("Xbox FAT plugin", "firstClusterSector = {0}", _firstClusterSector); uint[] rootDirectoryClusters = GetClusters(_superblock.rootDirectoryCluster); if(rootDirectoryClusters is null) return Errno.InvalidArgument; byte[] rootDirectoryBuffer = new byte[_bytesPerCluster * rootDirectoryClusters.Length]; AaruConsole.DebugWriteLine("Xbox FAT plugin", "Reading root directory"); for(int i = 0; i < rootDirectoryClusters.Length; i++) { buffer = imagePlugin.ReadSectors(_firstClusterSector + ((rootDirectoryClusters[i] - 1) * _sectorsPerCluster), _sectorsPerCluster); Array.Copy(buffer, 0, rootDirectoryBuffer, i * _bytesPerCluster, _bytesPerCluster); } _rootDirectory = new Dictionary<string, DirectoryEntry>(); int pos = 0; while(pos < rootDirectoryBuffer.Length) { DirectoryEntry entry = _littleEndian ? Marshal. ByteArrayToStructureLittleEndian<DirectoryEntry >(rootDirectoryBuffer, pos, Marshal.SizeOf<DirectoryEntry>()) : Marshal.ByteArrayToStructureBigEndian<DirectoryEntry>(rootDirectoryBuffer, pos, Marshal.SizeOf<DirectoryEntry>()); pos += Marshal.SizeOf<DirectoryEntry>(); if(entry.filenameSize == UNUSED_DIRENTRY || entry.filenameSize == FINISHED_DIRENTRY) break; if(entry.filenameSize == DELETED_DIRENTRY || entry.filenameSize > MAX_FILENAME) continue; string filename = Encoding.GetString(entry.filename, 0, entry.filenameSize); _rootDirectory.Add(filename, entry); } _cultureInfo = new CultureInfo("en-US", false); _directoryCache = new Dictionary<string, Dictionary<string, DirectoryEntry>>(); _mounted = true; return Errno.NoError; }
public Errno Mount(IMediaImage imagePlugin, Partition partition, Encoding encoding, Dictionary <string, string> options, string @namespace) { Encoding = encoding ?? Encoding.GetEncoding(1252); byte[] vdMagic = new byte[5]; // Volume Descriptor magic "CD001" byte[] hsMagic = new byte[5]; // Volume Descriptor magic "CDROM" options ??= GetDefaultOptions(); if (options.TryGetValue("debug", out string debugString)) { bool.TryParse(debugString, out _debug); } if (options.TryGetValue("use_path_table", out string usePathTableString)) { bool.TryParse(usePathTableString, out _usePathTable); } if (options.TryGetValue("use_trans_tbl", out string useTransTblString)) { bool.TryParse(useTransTblString, out _useTransTbl); } if (options.TryGetValue("use_evd", out string useEvdString)) { bool.TryParse(useEvdString, out _useEvd); } // Default namespace @namespace ??= "joliet"; switch (@namespace.ToLowerInvariant()) { case "normal": _namespace = Namespace.Normal; break; case "vms": _namespace = Namespace.Vms; break; case "joliet": _namespace = Namespace.Joliet; break; case "rrip": _namespace = Namespace.Rrip; break; case "romeo": _namespace = Namespace.Romeo; break; default: return(Errno.InvalidArgument); } PrimaryVolumeDescriptor?pvd = null; PrimaryVolumeDescriptor?jolietvd = null; BootRecord?bvd = null; HighSierraPrimaryVolumeDescriptor?hsvd = null; FileStructureVolumeDescriptor? fsvd = null; // ISO9660 is designed for 2048 bytes/sector devices if (imagePlugin.Info.SectorSize < 2048) { return(Errno.InvalidArgument); } // ISO9660 Primary Volume Descriptor starts at sector 16, so that's minimal size. if (partition.End < 16) { return(Errno.InvalidArgument); } ulong counter = 0; byte[] vdSector = imagePlugin.ReadSector(16 + counter + partition.Start); int xaOff = vdSector.Length == 2336 ? 8 : 0; Array.Copy(vdSector, 0x009 + xaOff, hsMagic, 0, 5); _highSierra = Encoding.GetString(hsMagic) == HIGH_SIERRA_MAGIC; int hsOff = 0; if (_highSierra) { hsOff = 8; } _cdi = false; List <ulong> bvdSectors = new List <ulong>(); List <ulong> pvdSectors = new List <ulong>(); List <ulong> svdSectors = new List <ulong>(); List <ulong> evdSectors = new List <ulong>(); List <ulong> vpdSectors = new List <ulong>(); while (true) { AaruConsole.DebugWriteLine("ISO9660 plugin", "Processing VD loop no. {0}", counter); // Seek to Volume Descriptor AaruConsole.DebugWriteLine("ISO9660 plugin", "Reading sector {0}", 16 + counter + partition.Start); byte[] vdSectorTmp = imagePlugin.ReadSector(16 + counter + partition.Start); vdSector = new byte[vdSectorTmp.Length - xaOff]; Array.Copy(vdSectorTmp, xaOff, vdSector, 0, vdSector.Length); byte vdType = vdSector[0 + hsOff]; // Volume Descriptor Type, should be 1 or 2. AaruConsole.DebugWriteLine("ISO9660 plugin", "VDType = {0}", vdType); if (vdType == 255) // Supposedly we are in the PVD. { if (counter == 0) { return(Errno.InvalidArgument); } break; } Array.Copy(vdSector, 0x001, vdMagic, 0, 5); Array.Copy(vdSector, 0x009, hsMagic, 0, 5); if (Encoding.GetString(vdMagic) != ISO_MAGIC && Encoding.GetString(hsMagic) != HIGH_SIERRA_MAGIC && Encoding.GetString(vdMagic) != CDI_MAGIC ) // Recognized, it is an ISO9660, now check for rest of data. { if (counter == 0) { return(Errno.InvalidArgument); } break; } _cdi |= Encoding.GetString(vdMagic) == CDI_MAGIC; switch (vdType) { case 0: { if (_debug) { bvdSectors.Add(16 + counter + partition.Start); } break; } case 1: { if (_highSierra) { hsvd = Marshal. ByteArrayToStructureLittleEndian <HighSierraPrimaryVolumeDescriptor>(vdSector); } else if (_cdi) { fsvd = Marshal.ByteArrayToStructureBigEndian <FileStructureVolumeDescriptor>(vdSector); } else { pvd = Marshal.ByteArrayToStructureLittleEndian <PrimaryVolumeDescriptor>(vdSector); } if (_debug) { pvdSectors.Add(16 + counter + partition.Start); } break; } case 2: { PrimaryVolumeDescriptor svd = Marshal.ByteArrayToStructureLittleEndian <PrimaryVolumeDescriptor>(vdSector); // TODO: Other escape sequences // Check if this is Joliet if (svd.version == 1) { if (svd.escape_sequences[0] == '%' && svd.escape_sequences[1] == '/') { if (svd.escape_sequences[2] == '@' || svd.escape_sequences[2] == 'C' || svd.escape_sequences[2] == 'E') { jolietvd = svd; } else { AaruConsole.DebugWriteLine("ISO9660 plugin", "Found unknown supplementary volume descriptor"); } } if (_debug) { svdSectors.Add(16 + counter + partition.Start); } } else { if (_debug) { evdSectors.Add(16 + counter + partition.Start); } if (_useEvd) { // Basically until escape sequences are implemented, let the user chose the encoding. // This is the same as user choosing Romeo namespace, but using the EVD instead of the PVD _namespace = Namespace.Romeo; pvd = svd; } } break; } case 3: { if (_debug) { vpdSectors.Add(16 + counter + partition.Start); } break; } } counter++; } DecodedVolumeDescriptor decodedVd; var decodedJolietVd = new DecodedVolumeDescriptor(); XmlFsType = new FileSystemType(); if (pvd == null && hsvd == null && fsvd == null) { AaruConsole.ErrorWriteLine("ERROR: Could not find primary volume descriptor"); return(Errno.InvalidArgument); } if (_highSierra) { decodedVd = DecodeVolumeDescriptor(hsvd.Value); } else if (_cdi) { decodedVd = DecodeVolumeDescriptor(fsvd.Value); } else { decodedVd = DecodeVolumeDescriptor(pvd.Value); } if (jolietvd != null) { decodedJolietVd = DecodeJolietDescriptor(jolietvd.Value); } if (_namespace != Namespace.Romeo) { Encoding = Encoding.ASCII; } string fsFormat; byte[] pathTableData; uint pathTableMsbLocation; uint pathTableLsbLocation = 0; // Initialize to 0 as ignored in CD-i _image = imagePlugin; if (_highSierra) { _blockSize = hsvd.Value.logical_block_size; pathTableData = ReadSingleExtent(hsvd.Value.path_table_size, Swapping.Swap(hsvd.Value.mandatory_path_table_msb)); fsFormat = "High Sierra Format"; pathTableMsbLocation = hsvd.Value.mandatory_path_table_msb; pathTableLsbLocation = hsvd.Value.mandatory_path_table_lsb; } else if (_cdi) { _blockSize = fsvd.Value.logical_block_size; pathTableData = ReadSingleExtent(fsvd.Value.path_table_size, fsvd.Value.path_table_addr); fsFormat = "CD-i"; pathTableMsbLocation = fsvd.Value.path_table_addr; // TODO: Until escape sequences are implemented this is the default CD-i encoding. Encoding = Encoding.GetEncoding("iso8859-1"); } else { _blockSize = pvd.Value.logical_block_size; pathTableData = ReadSingleExtent(pvd.Value.path_table_size, Swapping.Swap(pvd.Value.type_m_path_table)); fsFormat = "ISO9660"; pathTableMsbLocation = pvd.Value.type_m_path_table; pathTableLsbLocation = pvd.Value.type_l_path_table; } _pathTable = _highSierra ? DecodeHighSierraPathTable(pathTableData) : DecodePathTable(pathTableData); // High Sierra and CD-i do not support Joliet or RRIP if ((_highSierra || _cdi) && _namespace != Namespace.Normal && _namespace != Namespace.Vms) { _namespace = Namespace.Normal; } if (jolietvd is null && _namespace == Namespace.Joliet) { _namespace = Namespace.Normal; } uint rootLocation; uint rootSize; byte rootXattrLength = 0; if (!_cdi) { rootLocation = _highSierra ? hsvd.Value.root_directory_record.extent : pvd.Value.root_directory_record.extent; rootXattrLength = _highSierra ? hsvd.Value.root_directory_record.xattr_len : pvd.Value.root_directory_record.xattr_len; rootSize = _highSierra ? hsvd.Value.root_directory_record.size : pvd.Value.root_directory_record.size; if (pathTableData.Length > 1 && rootLocation != _pathTable[0].Extent) { AaruConsole.DebugWriteLine("ISO9660 plugin", "Path table and PVD do not point to the same location for the root directory!"); byte[] firstRootSector = ReadSector(rootLocation); bool pvdWrongRoot = false; if (_highSierra) { HighSierraDirectoryRecord rootEntry = Marshal.ByteArrayToStructureLittleEndian <HighSierraDirectoryRecord>(firstRootSector); if (rootEntry.extent != rootLocation) { pvdWrongRoot = true; } } else { DirectoryRecord rootEntry = Marshal.ByteArrayToStructureLittleEndian <DirectoryRecord>(firstRootSector); if (rootEntry.extent != rootLocation) { pvdWrongRoot = true; } } if (pvdWrongRoot) { AaruConsole.DebugWriteLine("ISO9660 plugin", "PVD does not point to correct root directory, checking path table..."); bool pathTableWrongRoot = false; rootLocation = _pathTable[0].Extent; firstRootSector = ReadSector(_pathTable[0].Extent); if (_highSierra) { HighSierraDirectoryRecord rootEntry = Marshal.ByteArrayToStructureLittleEndian <HighSierraDirectoryRecord>(firstRootSector); if (rootEntry.extent != rootLocation) { pathTableWrongRoot = true; } } else { DirectoryRecord rootEntry = Marshal.ByteArrayToStructureLittleEndian <DirectoryRecord>(firstRootSector); if (rootEntry.extent != rootLocation) { pathTableWrongRoot = true; } } if (pathTableWrongRoot) { AaruConsole.ErrorWriteLine("Cannot find root directory..."); return(Errno.InvalidArgument); } _usePathTable = true; } } } else { rootLocation = _pathTable[0].Extent; byte[] firstRootSector = ReadSector(rootLocation); CdiDirectoryRecord rootEntry = Marshal.ByteArrayToStructureBigEndian <CdiDirectoryRecord>(firstRootSector); rootSize = rootEntry.size; _usePathTable = _usePathTable || _pathTable.Length == 1; _useTransTbl = false; } // In case the path table is incomplete if (_usePathTable && pathTableData.Length == 1) { _usePathTable = false; } if (_usePathTable && !_cdi) { rootLocation = _pathTable[0].Extent; byte[] firstRootSector = ReadSector(rootLocation); if (_highSierra) { HighSierraDirectoryRecord rootEntry = Marshal.ByteArrayToStructureLittleEndian <HighSierraDirectoryRecord>(firstRootSector); rootSize = rootEntry.size; } else { DirectoryRecord rootEntry = Marshal.ByteArrayToStructureLittleEndian <DirectoryRecord>(firstRootSector); rootSize = rootEntry.size; } rootXattrLength = _pathTable[0].XattrLength; } try { _ = ReadSingleExtent(rootSize, rootLocation); } catch { return(Errno.InvalidArgument); } byte[] ipbinSector = ReadSector(partition.Start); CD.IPBin? segaCd = CD.DecodeIPBin(ipbinSector); Saturn.IPBin? saturn = Saturn.DecodeIPBin(ipbinSector); Dreamcast.IPBin?dreamcast = Dreamcast.DecodeIPBin(ipbinSector); if (_namespace == Namespace.Joliet || _namespace == Namespace.Rrip) { _usePathTable = false; _useTransTbl = false; } // Cannot traverse path table if we substitute the names for the ones in TRANS.TBL if (_useTransTbl) { _usePathTable = false; } if (_namespace != Namespace.Joliet) { _rootDirectoryCache = _cdi ? DecodeCdiDirectory(rootLocation + rootXattrLength, rootSize) : _highSierra ? DecodeHighSierraDirectory(rootLocation + rootXattrLength, rootSize) : DecodeIsoDirectory(rootLocation + rootXattrLength, rootSize); } XmlFsType.Type = fsFormat; if (jolietvd != null && (_namespace == Namespace.Joliet || _namespace == Namespace.Rrip)) { rootLocation = jolietvd.Value.root_directory_record.extent; rootXattrLength = jolietvd.Value.root_directory_record.xattr_len; rootSize = jolietvd.Value.root_directory_record.size; _joliet = true; _rootDirectoryCache = DecodeIsoDirectory(rootLocation + rootXattrLength, rootSize); XmlFsType.VolumeName = decodedJolietVd.VolumeIdentifier; if (string.IsNullOrEmpty(decodedJolietVd.SystemIdentifier) || decodedVd.SystemIdentifier.Length > decodedJolietVd.SystemIdentifier.Length) { XmlFsType.SystemIdentifier = decodedVd.SystemIdentifier; } else { XmlFsType.SystemIdentifier = string.IsNullOrEmpty(decodedJolietVd.SystemIdentifier) ? null : decodedJolietVd.SystemIdentifier; } if (string.IsNullOrEmpty(decodedJolietVd.VolumeSetIdentifier) || decodedVd.VolumeSetIdentifier.Length > decodedJolietVd.VolumeSetIdentifier.Length) { XmlFsType.VolumeSetIdentifier = decodedVd.VolumeSetIdentifier; } else { XmlFsType.VolumeSetIdentifier = string.IsNullOrEmpty(decodedJolietVd.VolumeSetIdentifier) ? null : decodedJolietVd.VolumeSetIdentifier; } if (string.IsNullOrEmpty(decodedJolietVd.PublisherIdentifier) || decodedVd.PublisherIdentifier.Length > decodedJolietVd.PublisherIdentifier.Length) { XmlFsType.PublisherIdentifier = decodedVd.PublisherIdentifier; } else { XmlFsType.PublisherIdentifier = string.IsNullOrEmpty(decodedJolietVd.PublisherIdentifier) ? null : decodedJolietVd.PublisherIdentifier; } if (string.IsNullOrEmpty(decodedJolietVd.DataPreparerIdentifier) || decodedVd.DataPreparerIdentifier.Length > decodedJolietVd.DataPreparerIdentifier.Length) { XmlFsType.DataPreparerIdentifier = decodedVd.DataPreparerIdentifier; } else { XmlFsType.DataPreparerIdentifier = string.IsNullOrEmpty(decodedJolietVd.DataPreparerIdentifier) ? null : decodedJolietVd.DataPreparerIdentifier; } if (string.IsNullOrEmpty(decodedJolietVd.ApplicationIdentifier) || decodedVd.ApplicationIdentifier.Length > decodedJolietVd.ApplicationIdentifier.Length) { XmlFsType.ApplicationIdentifier = decodedVd.ApplicationIdentifier; } else { XmlFsType.ApplicationIdentifier = string.IsNullOrEmpty(decodedJolietVd.ApplicationIdentifier) ? null : decodedJolietVd.ApplicationIdentifier; } XmlFsType.CreationDate = decodedJolietVd.CreationTime; XmlFsType.CreationDateSpecified = true; if (decodedJolietVd.HasModificationTime) { XmlFsType.ModificationDate = decodedJolietVd.ModificationTime; XmlFsType.ModificationDateSpecified = true; } if (decodedJolietVd.HasExpirationTime) { XmlFsType.ExpirationDate = decodedJolietVd.ExpirationTime; XmlFsType.ExpirationDateSpecified = true; } if (decodedJolietVd.HasEffectiveTime) { XmlFsType.EffectiveDate = decodedJolietVd.EffectiveTime; XmlFsType.EffectiveDateSpecified = true; } decodedVd = decodedJolietVd; } else { XmlFsType.SystemIdentifier = decodedVd.SystemIdentifier; XmlFsType.VolumeName = decodedVd.VolumeIdentifier; XmlFsType.VolumeSetIdentifier = decodedVd.VolumeSetIdentifier; XmlFsType.PublisherIdentifier = decodedVd.PublisherIdentifier; XmlFsType.DataPreparerIdentifier = decodedVd.DataPreparerIdentifier; XmlFsType.ApplicationIdentifier = decodedVd.ApplicationIdentifier; XmlFsType.CreationDate = decodedVd.CreationTime; XmlFsType.CreationDateSpecified = true; if (decodedVd.HasModificationTime) { XmlFsType.ModificationDate = decodedVd.ModificationTime; XmlFsType.ModificationDateSpecified = true; } if (decodedVd.HasExpirationTime) { XmlFsType.ExpirationDate = decodedVd.ExpirationTime; XmlFsType.ExpirationDateSpecified = true; } if (decodedVd.HasEffectiveTime) { XmlFsType.EffectiveDate = decodedVd.EffectiveTime; XmlFsType.EffectiveDateSpecified = true; } } if (_debug) { _rootDirectoryCache.Add("$", new DecodedDirectoryEntry { Extents = new List <(uint extent, uint size)> { (rootLocation, rootSize) }, Filename = "$", Size = rootSize, Timestamp = decodedVd.CreationTime });
public bool GetInformation(IMediaImage imagePlugin, out List <CommonTypes.Partition> partitions, ulong sectorOffset) { partitions = new List <CommonTypes.Partition>(); byte[] sector = imagePlugin.ReadSector(sectorOffset); if (sector.Length < 512) { return(false); } Label dvh = Marshal.ByteArrayToStructureBigEndian <Label>(sector); for (int i = 0; i < dvh.volume.Length; i++) { dvh.volume[i] = (Volume)Marshal.SwapStructureMembersEndian(dvh.volume[i]); } for (int i = 0; i < dvh.partitions.Length; i++) { dvh.partitions[i] = (Partition)Marshal.SwapStructureMembersEndian(dvh.partitions[i]); } AaruConsole.DebugWriteLine("SGIVH plugin", "dvh.magic = 0x{0:X8} (should be 0x{1:X8})", dvh.magic, SGI_MAGIC); if (dvh.magic != SGI_MAGIC) { return(false); } AaruConsole.DebugWriteLine("SGIVH plugin", "dvh.root_part_num = {0}", dvh.root_part_num); AaruConsole.DebugWriteLine("SGIVH plugin", "dvh.swap_part_num = {0}", dvh.swap_part_num); AaruConsole.DebugWriteLine("SGIVH plugin", "dvh.boot_file = \"{0}\"", StringHandlers.CToString(dvh.boot_file)); AaruConsole.DebugWriteLine("SGIVH plugin", "dvh.device_params.dp_skew = {0}", dvh.device_params.dp_skew); AaruConsole.DebugWriteLine("SGIVH plugin", "dvh.device_params.dp_gap1 = {0}", dvh.device_params.dp_gap1); AaruConsole.DebugWriteLine("SGIVH plugin", "dvh.device_params.dp_gap2 = {0}", dvh.device_params.dp_gap2); AaruConsole.DebugWriteLine("SGIVH plugin", "dvh.device_params.dp_spares_cyl = {0}", dvh.device_params.dp_spares_cyl); AaruConsole.DebugWriteLine("SGIVH plugin", "dvh.device_params.dp_cyls = {0}", dvh.device_params.dp_cyls); AaruConsole.DebugWriteLine("SGIVH plugin", "dvh.device_params.dp_shd0 = {0}", dvh.device_params.dp_shd0); AaruConsole.DebugWriteLine("SGIVH plugin", "dvh.device_params.dp_trks0 = {0}", dvh.device_params.dp_trks0); AaruConsole.DebugWriteLine("SGIVH plugin", "dvh.device_params.dp_ctq_depth = {0}", dvh.device_params.dp_ctq_depth); AaruConsole.DebugWriteLine("SGIVH plugin", "dvh.device_params.dp_cylshi = {0}", dvh.device_params.dp_cylshi); AaruConsole.DebugWriteLine("SGIVH plugin", "dvh.device_params.dp_secs = {0}", dvh.device_params.dp_secs); AaruConsole.DebugWriteLine("SGIVH plugin", "dvh.device_params.dp_secbytes = {0}", dvh.device_params.dp_secbytes); AaruConsole.DebugWriteLine("SGIVH plugin", "dvh.device_params.dp_interleave = {0}", dvh.device_params.dp_interleave); AaruConsole.DebugWriteLine("SGIVH plugin", "dvh.device_params.dp_flags = {0}", dvh.device_params.dp_flags); AaruConsole.DebugWriteLine("SGIVH plugin", "dvh.device_params.dp_datarate = {0}", dvh.device_params.dp_datarate); AaruConsole.DebugWriteLine("SGIVH plugin", "dvh.device_params.dp_nretries = {0}", dvh.device_params.dp_nretries); AaruConsole.DebugWriteLine("SGIVH plugin", "dvh.device_params.dp_mspw = {0}", dvh.device_params.dp_mspw); AaruConsole.DebugWriteLine("SGIVH plugin", "dvh.device_params.dp_xgap1 = {0}", dvh.device_params.dp_xgap1); AaruConsole.DebugWriteLine("SGIVH plugin", "dvh.device_params.dp_xsync = {0}", dvh.device_params.dp_xsync); AaruConsole.DebugWriteLine("SGIVH plugin", "dvh.device_params.dp_xrdly = {0}", dvh.device_params.dp_xrdly); AaruConsole.DebugWriteLine("SGIVH plugin", "dvh.device_params.dp_xgap2 = {0}", dvh.device_params.dp_xgap2); AaruConsole.DebugWriteLine("SGIVH plugin", "dvh.device_params.dp_xrgate = {0}", dvh.device_params.dp_xrgate); AaruConsole.DebugWriteLine("SGIVH plugin", "dvh.device_params.dp_xwcont = {0}", dvh.device_params.dp_xwcont); ulong counter = 0; for (int i = 0; i < dvh.partitions.Length; i++) { AaruConsole.DebugWriteLine("SGIVH plugin", "dvh.partitions[{0}].num_blocks = {1}", i, dvh.partitions[i].num_blocks); AaruConsole.DebugWriteLine("SGIVH plugin", "dvh.partitions[{0}].first_block = {1}", i, dvh.partitions[i].first_block); // TODO: Solve big endian marshal with enumerations dvh.partitions[i].type = (SGIType)Swapping.Swap((uint)dvh.partitions[i].type); AaruConsole.DebugWriteLine("SGIVH plugin", "dvh.partitions[{0}].type = {1}", i, dvh.partitions[i].type); var part = new CommonTypes.Partition { Start = (dvh.partitions[i].first_block * dvh.device_params.dp_secbytes) / imagePlugin.Info.SectorSize, Offset = dvh.partitions[i].first_block * dvh.device_params.dp_secbytes, Length = (dvh.partitions[i].num_blocks * dvh.device_params.dp_secbytes) / imagePlugin.Info.SectorSize, Size = dvh.partitions[i].num_blocks * dvh.device_params.dp_secbytes, Type = TypeToString(dvh.partitions[i].type), Sequence = counter, Scheme = Name }; if (part.Size <= 0 || dvh.partitions[i].type == SGIType.Header || dvh.partitions[i].type == SGIType.Volume) { continue; } partitions.Add(part); counter++; } return(true); }