Example #1
0
        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);
        }
Example #2
0
        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.");
            }
            }
        }
Example #3
0
File: Super.cs Project: paulyc/Aaru
        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;
        }
Example #4
0
        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
                });
Example #5
0
        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);
        }