Пример #1
0
        public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information,
                                   Encoding encoding)
        {
            Encoding    = encoding ?? Encoding.GetEncoding("macintosh");
            information = "";

            StringBuilder sb = new StringBuilder();

            byte[] bbSector  = null;
            byte[] mdbSector = null;
            ushort drSigWord;

            bool apmFromHddOnCd = false;

            if (imagePlugin.Info.SectorSize == 2352 || imagePlugin.Info.SectorSize == 2448 ||
                imagePlugin.Info.SectorSize == 2048)
            {
                byte[] tmpSector = imagePlugin.ReadSectors(partition.Start, 2);

                foreach (int offset in new[] { 0, 0x200, 0x400, 0x600, 0x800, 0xA00 })
                {
                    drSigWord = BigEndianBitConverter.ToUInt16(tmpSector, offset);
                    if (drSigWord != HFS_MAGIC)
                    {
                        continue;
                    }

                    bbSector  = new byte[1024];
                    mdbSector = new byte[512];
                    if (offset >= 0x400)
                    {
                        Array.Copy(tmpSector, offset - 0x400, bbSector, 0, 1024);
                    }
                    Array.Copy(tmpSector, offset, mdbSector, 0, 512);
                    apmFromHddOnCd = true;
                    break;
                }

                if (!apmFromHddOnCd)
                {
                    return;
                }
            }
            else
            {
                mdbSector = imagePlugin.ReadSector(2 + partition.Start);
                drSigWord = BigEndianBitConverter.ToUInt16(mdbSector, 0);

                if (drSigWord == HFS_MAGIC)
                {
                    bbSector = imagePlugin.ReadSector(partition.Start);
                }
                else
                {
                    return;
                }
            }

            HfsMasterDirectoryBlock mdb =
                BigEndianMarshal.ByteArrayToStructureBigEndian <HfsMasterDirectoryBlock>(mdbSector);
            HfsBootBlock bb = BigEndianMarshal.ByteArrayToStructureBigEndian <HfsBootBlock>(bbSector);

            sb.AppendLine("Apple Hierarchical File System");
            sb.AppendLine();
            if (apmFromHddOnCd)
            {
                sb.AppendLine("HFS uses 512 bytes/sector while device uses 2048 bytes/sector.").AppendLine();
            }
            sb.AppendLine("Master Directory Block:");
            sb.AppendFormat("Creation date: {0}", DateHandlers.MacToDateTime(mdb.drCrDate)).AppendLine();
            sb.AppendFormat("Last modification date: {0}", DateHandlers.MacToDateTime(mdb.drLsMod)).AppendLine();
            if (mdb.drVolBkUp > 0)
            {
                sb.AppendFormat("Last backup date: {0}", DateHandlers.MacToDateTime(mdb.drVolBkUp)).AppendLine();
                sb.AppendFormat("Backup sequence number: {0}", mdb.drVSeqNum).AppendLine();
            }
            else
            {
                sb.AppendLine("Volume has never been backed up");
            }

            if ((mdb.drAtrb & 0x80) == 0x80)
            {
                sb.AppendLine("Volume is locked by hardware.");
            }
            sb.AppendLine((mdb.drAtrb & 0x100) == 0x100 ? "Volume was unmonted." : "Volume is mounted.");
            if ((mdb.drAtrb & 0x200) == 0x200)
            {
                sb.AppendLine("Volume has spared bad blocks.");
            }
            if ((mdb.drAtrb & 0x400) == 0x400)
            {
                sb.AppendLine("Volume does not need cache.");
            }
            if ((mdb.drAtrb & 0x800) == 0x800)
            {
                sb.AppendLine("Boot volume is inconsistent.");
            }
            if ((mdb.drAtrb & 0x1000) == 0x1000)
            {
                sb.AppendLine("There are reused CNIDs.");
            }
            if ((mdb.drAtrb & 0x2000) == 0x2000)
            {
                sb.AppendLine("Volume is journaled.");
            }
            if ((mdb.drAtrb & 0x4000) == 0x4000)
            {
                sb.AppendLine("Volume is seriously inconsistent.");
            }
            if ((mdb.drAtrb & 0x8000) == 0x8000)
            {
                sb.AppendLine("Volume is locked by software.");
            }

            sb.AppendFormat("{0} files on root directory", mdb.drNmFls).AppendLine();
            sb.AppendFormat("{0} directories on root directory", mdb.drNmRtDirs).AppendLine();
            sb.AppendFormat("{0} files on volume", mdb.drFilCnt).AppendLine();
            sb.AppendFormat("{0} directories on volume", mdb.drDirCnt).AppendLine();
            sb.AppendFormat("Volume write count: {0}", mdb.drWrCnt).AppendLine();

            sb.AppendFormat("Volume bitmap starting sector (in 512-bytes): {0}", mdb.drVBMSt).AppendLine();
            sb.AppendFormat("Next allocation block: {0}.", mdb.drAllocPtr).AppendLine();
            sb.AppendFormat("{0} volume allocation blocks.", mdb.drNmAlBlks).AppendLine();
            sb.AppendFormat("{0} bytes per allocation block.", mdb.drAlBlkSiz).AppendLine();
            sb.AppendFormat("{0} bytes to allocate when extending a file.", mdb.drClpSiz).AppendLine();
            sb.AppendFormat("{0} bytes to allocate when extending a Extents B-Tree.", mdb.drXTClpSiz).AppendLine();
            sb.AppendFormat("{0} bytes to allocate when extending a Catalog B-Tree.", mdb.drCTClpSiz).AppendLine();
            sb.AppendFormat("Sector of first allocation block: {0}", mdb.drAlBlSt).AppendLine();
            sb.AppendFormat("Next unused CNID: {0}", mdb.drNxtCNID).AppendLine();
            sb.AppendFormat("{0} unused allocation blocks.", mdb.drFreeBks).AppendLine();

            sb.AppendFormat("{0} bytes in the Extents B-Tree", mdb.drXTFlSize).AppendLine();
            sb.AppendFormat("{0} bytes in the Catalog B-Tree", mdb.drCTFlSize).AppendLine();

            sb.AppendFormat("Volume name: {0}", StringHandlers.PascalToString(mdb.drVN, Encoding)).AppendLine();

            sb.AppendLine("Finder info:");
            sb.AppendFormat("CNID of bootable system's directory: {0}", mdb.drFndrInfo0).AppendLine();
            sb.AppendFormat("CNID of first-run application's directory: {0}", mdb.drFndrInfo1).AppendLine();
            sb.AppendFormat("CNID of previously opened directory: {0}", mdb.drFndrInfo2).AppendLine();
            sb.AppendFormat("CNID of bootable Mac OS 8 or 9 directory: {0}", mdb.drFndrInfo3).AppendLine();
            sb.AppendFormat("CNID of bootable Mac OS X directory: {0}", mdb.drFndrInfo5).AppendLine();
            if (mdb.drFndrInfo6 != 0 && mdb.drFndrInfo7 != 0)
            {
                sb.AppendFormat("Mac OS X Volume ID: {0:X8}{1:X8}", mdb.drFndrInfo6, mdb.drFndrInfo7).AppendLine();
            }

            if (mdb.drEmbedSigWord == HFSP_MAGIC)
            {
                sb.AppendLine("Volume wraps a HFS+ volume.");
                sb.AppendFormat("Starting block of the HFS+ volume: {0}", mdb.xdrStABNt).AppendLine();
                sb.AppendFormat("Allocations blocks of the HFS+ volume: {0}", mdb.xdrNumABlks).AppendLine();
            }
            else
            {
                sb.AppendFormat("{0} blocks in volume cache", mdb.drVCSize).AppendLine();
                sb.AppendFormat("{0} blocks in volume bitmap cache", mdb.drVBMCSize).AppendLine();
                sb.AppendFormat("{0} blocks in volume common cache", mdb.drCtlCSize).AppendLine();
            }

            if (bb.signature == HFSBB_MAGIC)
            {
                sb.AppendLine("Volume is bootable.");
                sb.AppendLine();
                sb.AppendLine("Boot Block:");
                if ((bb.boot_flags & 0x40) == 0x40)
                {
                    sb.AppendLine("Boot block should be executed.");
                }
                if ((bb.boot_flags & 0x80) == 0x80)
                {
                    sb.AppendLine("Boot block is in new unknown format.");
                }
                else
                {
                    if (bb.boot_flags > 0)
                    {
                        sb.AppendLine("Allocate secondary sound buffer at boot.");
                    }
                    else if (bb.boot_flags < 0)
                    {
                        sb.AppendLine("Allocate secondary sound and video buffers at boot.");
                    }

                    sb.AppendFormat("System filename: {0}", StringHandlers.PascalToString(bb.system_name, Encoding))
                    .AppendLine();
                    sb.AppendFormat("Finder filename: {0}", StringHandlers.PascalToString(bb.finder_name, Encoding))
                    .AppendLine();
                    sb.AppendFormat("Debugger filename: {0}", StringHandlers.PascalToString(bb.debug_name, Encoding))
                    .AppendLine();
                    sb.AppendFormat("Disassembler filename: {0}",
                                    StringHandlers.PascalToString(bb.disasm_name, Encoding)).AppendLine();
                    sb.AppendFormat("Startup screen filename: {0}",
                                    StringHandlers.PascalToString(bb.stupscr_name, Encoding)).AppendLine();
                    sb.AppendFormat("First program to execute at boot: {0}",
                                    StringHandlers.PascalToString(bb.bootup_name, Encoding)).AppendLine();
                    sb.AppendFormat("Clipboard filename: {0}", StringHandlers.PascalToString(bb.clipbrd_name, Encoding))
                    .AppendLine();
                    sb.AppendFormat("Maximum opened files: {0}", bb.max_files * 4).AppendLine();
                    sb.AppendFormat("Event queue size: {0}", bb.queue_size).AppendLine();
                    sb.AppendFormat("Heap size with 128KiB of RAM: {0} bytes", bb.heap_128k).AppendLine();
                    sb.AppendFormat("Heap size with 256KiB of RAM: {0} bytes", bb.heap_256k).AppendLine();
                    sb.AppendFormat("Heap size with 512KiB of RAM or more: {0} bytes", bb.heap_512k).AppendLine();
                }
            }
            else if (mdb.drFndrInfo0 != 0 || mdb.drFndrInfo3 != 0 || mdb.drFndrInfo5 != 0)
            {
                sb.AppendLine("Volume is bootable.");
            }
            else
            {
                sb.AppendLine("Volume is not bootable.");
            }

            information = sb.ToString();

            XmlFsType = new FileSystemType();
            if (mdb.drVolBkUp > 0)
            {
                XmlFsType.BackupDate          = DateHandlers.MacToDateTime(mdb.drVolBkUp);
                XmlFsType.BackupDateSpecified = true;
            }

            XmlFsType.Bootable = bb.signature == HFSBB_MAGIC || mdb.drFndrInfo0 != 0 || mdb.drFndrInfo3 != 0 ||
                                 mdb.drFndrInfo5 != 0;
            XmlFsType.Clusters    = mdb.drNmAlBlks;
            XmlFsType.ClusterSize = (int)mdb.drAlBlkSiz;
            if (mdb.drCrDate > 0)
            {
                XmlFsType.CreationDate          = DateHandlers.MacToDateTime(mdb.drCrDate);
                XmlFsType.CreationDateSpecified = true;
            }

            XmlFsType.Dirty                 = (mdb.drAtrb & 0x100) != 0x100;
            XmlFsType.Files                 = mdb.drFilCnt;
            XmlFsType.FilesSpecified        = true;
            XmlFsType.FreeClusters          = mdb.drFreeBks;
            XmlFsType.FreeClustersSpecified = true;
            if (mdb.drLsMod > 0)
            {
                XmlFsType.ModificationDate          = DateHandlers.MacToDateTime(mdb.drLsMod);
                XmlFsType.ModificationDateSpecified = true;
            }

            XmlFsType.Type       = "HFS";
            XmlFsType.VolumeName = StringHandlers.PascalToString(mdb.drVN, Encoding);
            if (mdb.drFndrInfo6 != 0 && mdb.drFndrInfo7 != 0)
            {
                XmlFsType.VolumeSerial = $"{mdb.drFndrInfo6:X8}{mdb.drFndrInfo7:X8}";
            }
        }
Пример #2
0
        public Errno Mount(IMediaImage imagePlugin, Partition partition, Encoding encoding,
                           Dictionary <string, string> options)
        {
            device         = imagePlugin;
            partitionStart = partition.Start;
            Encoding       = encoding ?? Encoding.GetEncoding("macintosh");
            if (options == null)
            {
                options = GetDefaultOptions();
            }
            if (options.TryGetValue("debug", out string debugString))
            {
                bool.TryParse(debugString, out debug);
            }
            volMDB = new MFS_MasterDirectoryBlock();

            mdbBlocks  = device.ReadSector(2 + partitionStart);
            bootBlocks = device.ReadSector(0 + partitionStart);

            BigEndianBitConverter.IsLittleEndian = BitConverter.IsLittleEndian;

            volMDB.drSigWord = BigEndianBitConverter.ToUInt16(mdbBlocks, 0x000);
            if (volMDB.drSigWord != MFS_MAGIC)
            {
                return(Errno.InvalidArgument);
            }

            volMDB.drCrDate   = BigEndianBitConverter.ToUInt32(mdbBlocks, 0x002);
            volMDB.drLsBkUp   = BigEndianBitConverter.ToUInt32(mdbBlocks, 0x006);
            volMDB.drAtrb     = BigEndianBitConverter.ToUInt16(mdbBlocks, 0x00A);
            volMDB.drNmFls    = BigEndianBitConverter.ToUInt16(mdbBlocks, 0x00C);
            volMDB.drDirSt    = BigEndianBitConverter.ToUInt16(mdbBlocks, 0x00E);
            volMDB.drBlLen    = BigEndianBitConverter.ToUInt16(mdbBlocks, 0x010);
            volMDB.drNmAlBlks = BigEndianBitConverter.ToUInt16(mdbBlocks, 0x012);
            volMDB.drAlBlkSiz = BigEndianBitConverter.ToUInt32(mdbBlocks, 0x014);
            volMDB.drClpSiz   = BigEndianBitConverter.ToUInt32(mdbBlocks, 0x018);
            volMDB.drAlBlSt   = BigEndianBitConverter.ToUInt16(mdbBlocks, 0x01C);
            volMDB.drNxtFNum  = BigEndianBitConverter.ToUInt32(mdbBlocks, 0x01E);
            volMDB.drFreeBks  = BigEndianBitConverter.ToUInt16(mdbBlocks, 0x022);
            volMDB.drVNSiz    = mdbBlocks[0x024];
            byte[] variableSize = new byte[volMDB.drVNSiz + 1];
            Array.Copy(mdbBlocks, 0x024, variableSize, 0, volMDB.drVNSiz + 1);
            volMDB.drVN = StringHandlers.PascalToString(variableSize, Encoding);

            directoryBlocks = device.ReadSectors(volMDB.drDirSt + partitionStart, volMDB.drBlLen);
            int       bytesInBlockMap        = volMDB.drNmAlBlks * 12 / 8 + volMDB.drNmAlBlks * 12 % 8;
            const int BYTES_BEFORE_BLOCK_MAP = 64;
            int       bytesInWholeMdb        = bytesInBlockMap + BYTES_BEFORE_BLOCK_MAP;
            int       sectorsInWholeMdb      = bytesInWholeMdb / (int)device.Info.SectorSize +
                                               bytesInWholeMdb % (int)device.Info.SectorSize;

            byte[] wholeMdb = device.ReadSectors(partitionStart + 2, (uint)sectorsInWholeMdb);
            blockMapBytes = new byte[bytesInBlockMap];
            Array.Copy(wholeMdb, BYTES_BEFORE_BLOCK_MAP, blockMapBytes, 0, blockMapBytes.Length);

            int offset = 0;

            blockMap = new uint[volMDB.drNmAlBlks + 2 + 1];
            for (int i = 2; i < volMDB.drNmAlBlks + 2; i += 8)
            {
                uint tmp1 = 0;
                uint tmp2 = 0;
                uint tmp3 = 0;

                if (offset + 4 <= blockMapBytes.Length)
                {
                    tmp1 = BigEndianBitConverter.ToUInt32(blockMapBytes, offset);
                }
                if (offset + 4 + 4 <= blockMapBytes.Length)
                {
                    tmp2 = BigEndianBitConverter.ToUInt32(blockMapBytes, offset + 4);
                }
                if (offset + 8 + 4 <= blockMapBytes.Length)
                {
                    tmp3 = BigEndianBitConverter.ToUInt32(blockMapBytes, offset + 8);
                }

                if (i < blockMap.Length)
                {
                    blockMap[i] = (tmp1 & 0xFFF00000) >> 20;
                }
                if (i + 2 < blockMap.Length)
                {
                    blockMap[i + 1] = (tmp1 & 0xFFF00) >> 8;
                }
                if (i + 3 < blockMap.Length)
                {
                    blockMap[i + 2] = ((tmp1 & 0xFF) << 4) + ((tmp2 & 0xF0000000) >> 28);
                }
                if (i + 4 < blockMap.Length)
                {
                    blockMap[i + 3] = (tmp2 & 0xFFF0000) >> 16;
                }
                if (i + 5 < blockMap.Length)
                {
                    blockMap[i + 4] = (tmp2 & 0xFFF0) >> 4;
                }
                if (i + 6 < blockMap.Length)
                {
                    blockMap[i + 5] = ((tmp2 & 0xF) << 8) + ((tmp3 & 0xFF000000) >> 24);
                }
                if (i + 7 < blockMap.Length)
                {
                    blockMap[i + 6] = (tmp3 & 0xFFF000) >> 12;
                }
                if (i + 8 < blockMap.Length)
                {
                    blockMap[i + 7] = tmp3 & 0xFFF;
                }

                offset += 12;
            }

            if (device.Info.ReadableSectorTags.Contains(SectorTagType.AppleSectorTag))
            {
                mdbTags       = device.ReadSectorTag(2 + partitionStart, SectorTagType.AppleSectorTag);
                bootTags      = device.ReadSectorTag(0 + partitionStart, SectorTagType.AppleSectorTag);
                directoryTags = device.ReadSectorsTag(volMDB.drDirSt + partitionStart, volMDB.drBlLen,
                                                      SectorTagType.AppleSectorTag);
                bitmapTags = device.ReadSectorsTag(partitionStart + 2, (uint)sectorsInWholeMdb,
                                                   SectorTagType.AppleSectorTag);
            }

            sectorsPerBlock = (int)(volMDB.drAlBlkSiz / device.Info.SectorSize);

            if (!FillDirectory())
            {
                return(Errno.InvalidArgument);
            }

            mounted = true;

            ushort bbSig = BigEndianBitConverter.ToUInt16(bootBlocks, 0x000);

            if (bbSig != MFSBB_MAGIC)
            {
                bootBlocks = null;
            }

            XmlFsType = new FileSystemType();
            if (volMDB.drLsBkUp > 0)
            {
                XmlFsType.BackupDate          = DateHandlers.MacToDateTime(volMDB.drLsBkUp);
                XmlFsType.BackupDateSpecified = true;
            }

            XmlFsType.Bootable    = bbSig == MFSBB_MAGIC;
            XmlFsType.Clusters    = volMDB.drNmAlBlks;
            XmlFsType.ClusterSize = (int)volMDB.drAlBlkSiz;
            if (volMDB.drCrDate > 0)
            {
                XmlFsType.CreationDate          = DateHandlers.MacToDateTime(volMDB.drCrDate);
                XmlFsType.CreationDateSpecified = true;
            }

            XmlFsType.Files                 = volMDB.drNmFls;
            XmlFsType.FilesSpecified        = true;
            XmlFsType.FreeClusters          = volMDB.drFreeBks;
            XmlFsType.FreeClustersSpecified = true;
            XmlFsType.Type       = "MFS";
            XmlFsType.VolumeName = volMDB.drVN;

            return(Errno.NoError);
        }
Пример #3
0
        public bool GetInformation(IMediaImage imagePlugin, out List <Partition> partitions, ulong sectorOffset)
        {
            partitions = new List <Partition>();

            if (sectorOffset + 2 >= imagePlugin.Info.Sectors)
            {
                return(false);
            }

            byte[]    hdrBytes = imagePlugin.ReadSector(1 + sectorOffset);
            GptHeader hdr;

            ulong signature  = BitConverter.ToUInt64(hdrBytes, 0);
            bool  misaligned = false;

            DicConsole.DebugWriteLine("GPT Plugin", "hdr.signature = 0x{0:X16}", signature);

            if (signature != GPT_MAGIC)
            {
                if (imagePlugin.Info.XmlMediaType == XmlMediaType.OpticalDisc)
                {
                    hdrBytes  = imagePlugin.ReadSector(sectorOffset);
                    signature = BitConverter.ToUInt64(hdrBytes, 512);
                    DicConsole.DebugWriteLine("GPT Plugin", "hdr.signature @ 0x200 = 0x{0:X16}", signature);
                    if (signature == GPT_MAGIC)
                    {
                        DicConsole.DebugWriteLine("GPT Plugin", "Found unaligned signature", signature);
                        byte[] real = new byte[512];
                        Array.Copy(hdrBytes, 512, real, 0, 512);
                        hdrBytes   = real;
                        misaligned = true;
                    }
                    else
                    {
                        return(false);
                    }
                }
                else
                {
                    return(false);
                }
            }

            try
            {
                GCHandle handle = GCHandle.Alloc(hdrBytes, GCHandleType.Pinned);
                hdr = (GptHeader)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(GptHeader));
                handle.Free();
            }
            catch { return(false); }

            DicConsole.DebugWriteLine("GPT Plugin", "hdr.revision = 0x{0:X8}", hdr.revision);
            DicConsole.DebugWriteLine("GPT Plugin", "hdr.headerSize = {0}", hdr.headerSize);
            DicConsole.DebugWriteLine("GPT Plugin", "hdr.headerCrc = 0x{0:X8}", hdr.headerCrc);
            DicConsole.DebugWriteLine("GPT Plugin", "hdr.reserved = 0x{0:X8}", hdr.reserved);
            DicConsole.DebugWriteLine("GPT Plugin", "hdr.myLBA = {0}", hdr.myLBA);
            DicConsole.DebugWriteLine("GPT Plugin", "hdr.alternateLBA = {0}", hdr.alternateLBA);
            DicConsole.DebugWriteLine("GPT Plugin", "hdr.firstUsableLBA = {0}", hdr.firstUsableLBA);
            DicConsole.DebugWriteLine("GPT Plugin", "hdr.lastUsableLBA = {0}", hdr.lastUsableLBA);
            DicConsole.DebugWriteLine("GPT Plugin", "hdr.diskGuid = {0}", hdr.diskGuid);
            DicConsole.DebugWriteLine("GPT Plugin", "hdr.entryLBA = {0}", hdr.entryLBA);
            DicConsole.DebugWriteLine("GPT Plugin", "hdr.entries = {0}", hdr.entries);
            DicConsole.DebugWriteLine("GPT Plugin", "hdr.entriesSize = {0}", hdr.entriesSize);
            DicConsole.DebugWriteLine("GPT Plugin", "hdr.entriesCrc = 0x{0:X8}", hdr.entriesCrc);

            if (hdr.signature != GPT_MAGIC)
            {
                return(false);
            }

            if (hdr.myLBA != 1 + sectorOffset)
            {
                return(false);
            }

            uint divisor, modulo, sectorSize;

            if (misaligned)
            {
                divisor    = 4;
                modulo     = (uint)(hdr.entryLBA % divisor);
                sectorSize = 512;
            }
            else
            {
                divisor    = 1;
                modulo     = 0;
                sectorSize = imagePlugin.Info.SectorSize;
            }

            uint totalEntriesSectors = hdr.entries * hdr.entriesSize / imagePlugin.Info.SectorSize;

            if (hdr.entries * hdr.entriesSize % imagePlugin.Info.SectorSize > 0)
            {
                totalEntriesSectors++;
            }

            byte[] temp         = imagePlugin.ReadSectors(hdr.entryLBA / divisor, totalEntriesSectors + modulo);
            byte[] entriesBytes = new byte[temp.Length - modulo * 512];
            Array.Copy(temp, modulo * 512, entriesBytes, 0, entriesBytes.Length);
            List <GptEntry> entries = new List <GptEntry>();

            for (int i = 0; i < hdr.entries; i++)
            {
                try
                {
                    byte[] entryBytes = new byte[hdr.entriesSize];
                    Array.Copy(entriesBytes, hdr.entriesSize * i, entryBytes, 0, hdr.entriesSize);
                    GCHandle handle = GCHandle.Alloc(entryBytes, GCHandleType.Pinned);
                    GptEntry entry  = (GptEntry)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(GptEntry));
                    handle.Free();
                    entries.Add(entry);
                }
#pragma warning disable RECS0022 // A catch clause that catches System.Exception and has an empty body
                catch
                {
                    // ignored
                }
#pragma warning restore RECS0022 // A catch clause that catches System.Exception and has an empty body
            }

            if (entries.Count == 0)
            {
                return(false);
            }

            ulong pseq = 0;

            foreach (GptEntry entry in
                     entries.Where(entry => entry.partitionType != Guid.Empty && entry.partitionId != Guid.Empty))
            {
                DicConsole.DebugWriteLine("GPT Plugin", "entry.partitionType = {0}", entry.partitionType);
                DicConsole.DebugWriteLine("GPT Plugin", "entry.partitionId = {0}", entry.partitionId);
                DicConsole.DebugWriteLine("GPT Plugin", "entry.startLBA = {0}", entry.startLBA);
                DicConsole.DebugWriteLine("GPT Plugin", "entry.endLBA = {0}", entry.endLBA);
                DicConsole.DebugWriteLine("GPT Plugin", "entry.attributes = 0x{0:X16}", entry.attributes);
                DicConsole.DebugWriteLine("GPT Plugin", "entry.name = {0}", entry.name);

                if (entry.startLBA / divisor > imagePlugin.Info.Sectors ||
                    entry.endLBA / divisor > imagePlugin.Info.Sectors)
                {
                    return(false);
                }

                Partition part = new Partition
                {
                    Description = $"ID: {entry.partitionId}",
                    Size        = (entry.endLBA - entry.startLBA + 1) * sectorSize,
                    Name        = entry.name,
                    Length      = (entry.endLBA - entry.startLBA + 1) / divisor,
                    Sequence    = pseq++,
                    Offset      = entry.startLBA * sectorSize,
                    Start       = entry.startLBA / divisor,
                    Type        = GetGuidTypeName(entry.partitionType),
                    Scheme      = Name
                };
                DicConsole.DebugWriteLine("GPT Plugin", "part.PartitionType = {0}", part.Type);
                partitions.Add(part);
            }

            return(true);
        }
Пример #4
0
        public bool Identify(IMediaImage imagePlugin, Partition partition)
        {
            if (imagePlugin.Info.SectorSize < 512)
            {
                return(false);
            }

            // Misaligned
            if (imagePlugin.Info.XmlMediaType == XmlMediaType.OpticalDisc)
            {
                uint sbSize = (uint)((Marshal.SizeOf <EFS_Superblock>() + 0x200) / imagePlugin.Info.SectorSize);
                if ((Marshal.SizeOf <EFS_Superblock>() + 0x200) % imagePlugin.Info.SectorSize != 0)
                {
                    sbSize++;
                }

                byte[] sector = imagePlugin.ReadSectors(partition.Start, sbSize);
                if (sector.Length < Marshal.SizeOf <EFS_Superblock>())
                {
                    return(false);
                }

                byte[] sbpiece = new byte[Marshal.SizeOf <EFS_Superblock>()];

                Array.Copy(sector, 0x200, sbpiece, 0, Marshal.SizeOf <EFS_Superblock>());

                EFS_Superblock efsSb = Marshal.ByteArrayToStructureBigEndian <EFS_Superblock>(sbpiece);

                DicConsole.DebugWriteLine("EFS plugin", "magic at 0x{0:X3} = 0x{1:X8} (expected 0x{2:X8} or 0x{3:X8})",
                                          0x200, efsSb.sb_magic, EFS_MAGIC, EFS_MAGIC_NEW);

                if (efsSb.sb_magic == EFS_MAGIC || efsSb.sb_magic == EFS_MAGIC_NEW)
                {
                    return(true);
                }
            }
            else
            {
                uint sbSize = (uint)(Marshal.SizeOf <EFS_Superblock>() / imagePlugin.Info.SectorSize);
                if (Marshal.SizeOf <EFS_Superblock>() % imagePlugin.Info.SectorSize != 0)
                {
                    sbSize++;
                }

                byte[] sector = imagePlugin.ReadSectors(partition.Start + 1, sbSize);
                if (sector.Length < Marshal.SizeOf <EFS_Superblock>())
                {
                    return(false);
                }

                EFS_Superblock efsSb = Marshal.ByteArrayToStructureBigEndian <EFS_Superblock>(sector);

                DicConsole.DebugWriteLine("EFS plugin", "magic at {0} = 0x{1:X8} (expected 0x{2:X8} or 0x{3:X8})", 1,
                                          efsSb.sb_magic, EFS_MAGIC, EFS_MAGIC_NEW);

                if (efsSb.sb_magic == EFS_MAGIC || efsSb.sb_magic == EFS_MAGIC_NEW)
                {
                    return(true);
                }
            }

            return(false);
        }
Пример #5
0
        public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information,
                                   Encoding encoding)
        {
            Encoding    = encoding ?? Encoding.UTF8;
            information = "";
            if (imagePlugin.Info.SectorSize < 512)
            {
                return;
            }

            uint sbAddr = NILFS2_SUPER_OFFSET / imagePlugin.Info.SectorSize;

            if (sbAddr == 0)
            {
                sbAddr = 1;
            }

            uint sbSize = (uint)(Marshal.SizeOf <NILFS2_Superblock>() / imagePlugin.Info.SectorSize);

            if (Marshal.SizeOf <NILFS2_Superblock>() % imagePlugin.Info.SectorSize != 0)
            {
                sbSize++;
            }

            byte[] sector = imagePlugin.ReadSectors(partition.Start + sbAddr, sbSize);
            if (sector.Length < Marshal.SizeOf <NILFS2_Superblock>())
            {
                return;
            }

            NILFS2_Superblock nilfsSb = Marshal.ByteArrayToStructureLittleEndian <NILFS2_Superblock>(sector);

            if (nilfsSb.magic != NILFS2_MAGIC)
            {
                return;
            }

            StringBuilder sb = new StringBuilder();

            sb.AppendLine("NILFS2 filesystem");
            sb.AppendFormat("Version {0}.{1}", nilfsSb.rev_level, nilfsSb.minor_rev_level).AppendLine();
            sb.AppendFormat("{0} bytes per block", 1 << (int)(nilfsSb.log_block_size + 10)).AppendLine();
            sb.AppendFormat("{0} bytes in volume", nilfsSb.dev_size).AppendLine();
            sb.AppendFormat("{0} blocks per segment", nilfsSb.blocks_per_segment).AppendLine();
            sb.AppendFormat("{0} segments", nilfsSb.nsegments).AppendLine();
            if (nilfsSb.creator_os == 0)
            {
                sb.AppendLine("Filesystem created on Linux");
            }
            else
            {
                sb.AppendFormat("Creator OS code: {0}", nilfsSb.creator_os).AppendLine();
            }
            sb.AppendFormat("{0} bytes per inode", nilfsSb.inode_size).AppendLine();
            sb.AppendFormat("Volume UUID: {0}", nilfsSb.uuid).AppendLine();
            sb.AppendFormat("Volume name: {0}", StringHandlers.CToString(nilfsSb.volume_name, Encoding)).AppendLine();
            sb.AppendFormat("Volume created on {0}", DateHandlers.UnixUnsignedToDateTime(nilfsSb.ctime)).AppendLine();
            sb.AppendFormat("Volume last mounted on {0}", DateHandlers.UnixUnsignedToDateTime(nilfsSb.mtime))
            .AppendLine();
            sb.AppendFormat("Volume last written on {0}", DateHandlers.UnixUnsignedToDateTime(nilfsSb.wtime))
            .AppendLine();

            information = sb.ToString();

            XmlFsType = new FileSystemType
            {
                Type                      = "NILFS2 filesystem",
                ClusterSize               = (uint)(1 << (int)(nilfsSb.log_block_size + 10)),
                VolumeName                = StringHandlers.CToString(nilfsSb.volume_name, Encoding),
                VolumeSerial              = nilfsSb.uuid.ToString(),
                CreationDate              = DateHandlers.UnixUnsignedToDateTime(nilfsSb.ctime),
                CreationDateSpecified     = true,
                ModificationDate          = DateHandlers.UnixUnsignedToDateTime(nilfsSb.wtime),
                ModificationDateSpecified = true
            };
            if (nilfsSb.creator_os == 0)
            {
                XmlFsType.SystemIdentifier = "Linux";
            }
            XmlFsType.Clusters = nilfsSb.dev_size / XmlFsType.ClusterSize;
        }
Пример #6
0
        public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information,
                                   Encoding encoding)
        {
            Encoding    = encoding ?? Encoding.GetEncoding("iso-8859-15");
            information = "";

            var sb = new StringBuilder();

            SuperBlock superBlock;

            uint run = HAMMER_VOLHDR_SIZE / imagePlugin.Info.SectorSize;

            if (HAMMER_VOLHDR_SIZE % imagePlugin.Info.SectorSize > 0)
            {
                run++;
            }

            byte[] sbSector = imagePlugin.ReadSectors(partition.Start, run);

            ulong magic = BitConverter.ToUInt64(sbSector, 0);

            superBlock = magic == HAMMER_FSBUF_VOLUME?Marshal.ByteArrayToStructureLittleEndian <SuperBlock>(sbSector)
                             : Marshal.ByteArrayToStructureBigEndian <SuperBlock>(sbSector);

            sb.AppendLine("HAMMER filesystem");

            sb.AppendFormat("Volume version: {0}", superBlock.vol_version).AppendLine();

            sb.AppendFormat("Volume {0} of {1} on this filesystem", superBlock.vol_no + 1, superBlock.vol_count).
            AppendLine();

            sb.AppendFormat("Volume name: {0}", StringHandlers.CToString(superBlock.vol_label, Encoding)).AppendLine();
            sb.AppendFormat("Volume serial: {0}", superBlock.vol_fsid).AppendLine();
            sb.AppendFormat("Filesystem type: {0}", superBlock.vol_fstype).AppendLine();
            sb.AppendFormat("Boot area starts at {0}", superBlock.vol_bot_beg).AppendLine();
            sb.AppendFormat("Memory log starts at {0}", superBlock.vol_mem_beg).AppendLine();
            sb.AppendFormat("First volume buffer starts at {0}", superBlock.vol_buf_beg).AppendLine();
            sb.AppendFormat("Volume ends at {0}", superBlock.vol_buf_end).AppendLine();

            XmlFsType = new FileSystemType
            {
                Clusters     = partition.Size / HAMMER_BIGBLOCK_SIZE,
                ClusterSize  = HAMMER_BIGBLOCK_SIZE,
                Dirty        = false,
                Type         = "HAMMER",
                VolumeName   = StringHandlers.CToString(superBlock.vol_label, Encoding),
                VolumeSerial = superBlock.vol_fsid.ToString()
            };

            if (superBlock.vol_no == superBlock.vol_rootvol)
            {
                sb.AppendFormat("Filesystem contains {0} \"big-blocks\" ({1} bytes)", superBlock.vol0_stat_bigblocks,
                                superBlock.vol0_stat_bigblocks * HAMMER_BIGBLOCK_SIZE).AppendLine();

                sb.AppendFormat("Filesystem has {0} \"big-blocks\" free ({1} bytes)",
                                superBlock.vol0_stat_freebigblocks,
                                superBlock.vol0_stat_freebigblocks * HAMMER_BIGBLOCK_SIZE).AppendLine();

                sb.AppendFormat("Filesystem has {0} inode used", superBlock.vol0_stat_inodes).AppendLine();

                XmlFsType.Clusters              = (ulong)superBlock.vol0_stat_bigblocks;
                XmlFsType.FreeClusters          = (ulong)superBlock.vol0_stat_freebigblocks;
                XmlFsType.FreeClustersSpecified = true;
                XmlFsType.Files          = (ulong)superBlock.vol0_stat_inodes;
                XmlFsType.FilesSpecified = true;
            }

            // 0 ?
            //sb.AppendFormat("Volume header CRC: 0x{0:X8}", afs_sb.vol_crc).AppendLine();

            information = sb.ToString();
        }
Пример #7
0
        public bool Identify(IMediaImage imagePlugin, Partition partition)
        {
            if (partition.Start >= partition.End)
            {
                return(false);
            }

            // Boot block is unless defined otherwise, 2 blocks
            // Funny, you may need boot block to find root block if it's not in standard place just to know size of
            // block size and then read the whole boot block.
            // However while you can set a block size different from the sector size on formatting, the bootblock block
            // size for floppies is the sector size, and for RDB is usually is the hard disk sector size,
            // so this is not entirely wrong...
            byte[]    sector = imagePlugin.ReadSectors(0 + partition.Start, 2);
            BootBlock bblk   = Marshal.ByteArrayToStructureBigEndian <BootBlock>(sector);

            // AROS boot floppies...
            if (sector.Length >= 512 &&
                sector[510] == 0x55 &&
                sector[511] == 0xAA &&
                (bblk.diskType & FFS_MASK) != FFS_MASK &&
                (bblk.diskType & MUFS_MASK) != MUFS_MASK)
            {
                sector = imagePlugin.ReadSectors(1 + partition.Start, 2);
                bblk   = Marshal.ByteArrayToStructureBigEndian <BootBlock>(sector);
            }

            // Not FFS or MuFS?
            if ((bblk.diskType & FFS_MASK) != FFS_MASK &&
                (bblk.diskType & MUFS_MASK) != MUFS_MASK)
            {
                return(false);
            }

            // Clear checksum on sector
            sector[4] = sector[5] = sector[6] = sector[7] = 0;
            uint bsum = AmigaBootChecksum(sector);

            AaruConsole.DebugWriteLine("AmigaDOS plugin", "bblk.checksum = 0x{0:X8}", bblk.checksum);
            AaruConsole.DebugWriteLine("AmigaDOS plugin", "bsum = 0x{0:X8}", bsum);

            ulong bRootPtr = 0;

            // If bootblock is correct, let's take its rootblock pointer
            if (bsum == bblk.checksum)
            {
                bRootPtr = bblk.root_ptr + partition.Start;
                AaruConsole.DebugWriteLine("AmigaDOS plugin", "Bootblock points to {0} as Rootblock", bRootPtr);
            }

            ulong[] rootPtrs =
            {
                bRootPtr + partition.Start,                                            ((((partition.End - partition.Start) + 1) / 2) + partition.Start) - 2,
                ((((partition.End - partition.Start) + 1) / 2) + partition.Start) - 1,
                (((partition.End - partition.Start) + 1) / 2) + partition.Start,
                (((partition.End - partition.Start) + 1) / 2) + partition.Start + 4
            };

            var rblk = new RootBlock();

            // So to handle even number of sectors
            foreach (ulong rootPtr in rootPtrs.Where(rootPtr => rootPtr < partition.End && rootPtr >= partition.Start))
            {
                AaruConsole.DebugWriteLine("AmigaDOS plugin", "Searching for Rootblock in sector {0}", rootPtr);

                sector = imagePlugin.ReadSector(rootPtr);

                rblk.type = BigEndianBitConverter.ToUInt32(sector, 0x00);
                AaruConsole.DebugWriteLine("AmigaDOS plugin", "rblk.type = {0}", rblk.type);

                if (rblk.type != TYPE_HEADER)
                {
                    continue;
                }

                rblk.hashTableSize = BigEndianBitConverter.ToUInt32(sector, 0x0C);

                AaruConsole.DebugWriteLine("AmigaDOS plugin", "rblk.hashTableSize = {0}", rblk.hashTableSize);

                uint blockSize       = (rblk.hashTableSize + 56) * 4;
                uint sectorsPerBlock = (uint)(blockSize / sector.Length);

                AaruConsole.DebugWriteLine("AmigaDOS plugin", "blockSize = {0}", blockSize);
                AaruConsole.DebugWriteLine("AmigaDOS plugin", "sectorsPerBlock = {0}", sectorsPerBlock);

                if (blockSize % sector.Length > 0)
                {
                    sectorsPerBlock++;
                }

                if (rootPtr + sectorsPerBlock >= partition.End)
                {
                    continue;
                }

                sector = imagePlugin.ReadSectors(rootPtr, sectorsPerBlock);

                // Clear checksum on sector
                rblk.checksum = BigEndianBitConverter.ToUInt32(sector, 20);
                sector[20]    = sector[21] = sector[22] = sector[23] = 0;
                uint rsum = AmigaChecksum(sector);

                AaruConsole.DebugWriteLine("AmigaDOS plugin", "rblk.checksum = 0x{0:X8}", rblk.checksum);
                AaruConsole.DebugWriteLine("AmigaDOS plugin", "rsum = 0x{0:X8}", rsum);

                rblk.sec_type = BigEndianBitConverter.ToUInt32(sector, sector.Length - 4);
                AaruConsole.DebugWriteLine("AmigaDOS plugin", "rblk.sec_type = {0}", rblk.sec_type);

                if (rblk.sec_type == SUBTYPE_ROOT &&
                    rblk.checksum == rsum)
                {
                    return(true);
                }
            }

            return(false);
        }
Пример #8
0
        public bool Identify(IMediaImage imagePlugin, Partition partition)
        {
            if (2 + partition.Start >= partition.End)
            {
                return(false);
            }

            byte sb_size_in_sectors;

            if (imagePlugin.Info.SectorSize <= 0x400
                ) // Check if underlying device sector size is smaller than SuperBlock size
            {
                sb_size_in_sectors = (byte)(0x400 / imagePlugin.Info.SectorSize);
            }
            else
            {
                sb_size_in_sectors = 1;  // If not a single sector can store it
            }
            if (partition.End <= partition.Start + 4 * (ulong)sb_size_in_sectors + sb_size_in_sectors
                ) // Device must be bigger than SB location + SB size + offset
            {
                return(false);
            }

            // Sectors in a cylinder
            int spc = (int)(imagePlugin.Info.Heads * imagePlugin.Info.SectorsPerTrack);

            // Superblock can start on 0x000, 0x200, 0x600 and 0x800, not aligned, so we assume 16 (128 bytes/sector) sectors as a safe value
            int[] locations =
            {
                0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
                // Superblock can also skip one cylinder (for boot)
                spc
            };

            foreach (byte[] sb_sector in locations.TakeWhile(i => (ulong)i + partition.Start + sb_size_in_sectors < imagePlugin.Info.Sectors)
                     .Select(i => imagePlugin.ReadSectors((ulong)i + partition.Start,
                                                          sb_size_in_sectors)))
            {
                uint magic = BitConverter.ToUInt32(sb_sector, 0x3F8);

                if (magic == XENIX_MAGIC || magic == XENIX_CIGAM || magic == SYSV_MAGIC || magic == SYSV_CIGAM)
                {
                    return(true);
                }

                magic = BitConverter.ToUInt32(sb_sector, 0x1F8); // System V magic location

                if (magic == SYSV_MAGIC || magic == SYSV_CIGAM)
                {
                    return(true);
                }

                magic = BitConverter.ToUInt32(sb_sector, 0x1F0); // XENIX 3 magic location

                if (magic == XENIX_MAGIC || magic == XENIX_CIGAM)
                {
                    return(true);
                }

                byte[] coherent_string = new byte[6];
                Array.Copy(sb_sector, 0x1E4, coherent_string, 0, 6); // Coherent UNIX s_fname location
                string s_fname = StringHandlers.CToString(coherent_string);
                Array.Copy(sb_sector, 0x1EA, coherent_string, 0, 6); // Coherent UNIX s_fpack location
                string s_fpack = StringHandlers.CToString(coherent_string);

                if (s_fname == COH_FNAME && s_fpack == COH_FPACK || s_fname == COH_XXXXX && s_fpack == COH_XXXXX ||
                    s_fname == COH_XXXXS && s_fpack == COH_XXXXN)
                {
                    return(true);
                }

                // Now try to identify 7th edition
                uint   s_fsize  = BitConverter.ToUInt32(sb_sector, 0x002);
                ushort s_nfree  = BitConverter.ToUInt16(sb_sector, 0x006);
                ushort s_ninode = BitConverter.ToUInt16(sb_sector, 0x0D0);

                if (s_fsize <= 0 || s_fsize >= 0xFFFFFFFF || s_nfree <= 0 || s_nfree >= 0xFFFF || s_ninode <= 0 ||
                    s_ninode >= 0xFFFF)
                {
                    continue;
                }

                if ((s_fsize & 0xFF) == 0x00 && (s_nfree & 0xFF) == 0x00 && (s_ninode & 0xFF) == 0x00)
                {
                    // Byteswap
                    s_fsize = ((s_fsize & 0xFF) << 24) + ((s_fsize & 0xFF00) << 8) + ((s_fsize & 0xFF0000) >> 8) +
                              ((s_fsize & 0xFF000000) >> 24);
                    s_nfree  = (ushort)(s_nfree >> 8);
                    s_ninode = (ushort)(s_ninode >> 8);
                }

                if ((s_fsize & 0xFF000000) != 0x00 || (s_nfree & 0xFF00) != 0x00 ||
                    (s_ninode & 0xFF00) != 0x00)
                {
                    continue;
                }

                if (s_fsize >= V7_MAXSIZE || s_nfree >= V7_NICFREE || s_ninode >= V7_NICINOD)
                {
                    continue;
                }

                if (s_fsize * 1024 == (partition.End - partition.Start) * imagePlugin.Info.SectorSize ||
                    s_fsize * 512 == (partition.End - partition.Start) * imagePlugin.Info.SectorSize)
                {
                    return(true);
                }
            }

            return(false);
        }
Пример #9
0
        public bool GetInformation(IMediaImage imagePlugin, out List <Partition> partitions, ulong sectorOffset)
        {
            uint sectorSize;

            if (imagePlugin.Info.SectorSize == 2352 || imagePlugin.Info.SectorSize == 2448)
            {
                sectorSize = 2048;
            }
            else
            {
                sectorSize =
                    imagePlugin.Info.SectorSize;
            }

            partitions = new List <Partition>();

            if (sectorOffset + 2 >= imagePlugin.Info.Sectors)
            {
                return(false);
            }

            byte[] ddmSector = imagePlugin.ReadSector(sectorOffset);

            ushort maxDrivers = 61;

            if (sectorSize == 256)
            {
                byte[] tmp = new byte[512];
                Array.Copy(ddmSector, 0, tmp, 0, 256);
                ddmSector  = tmp;
                maxDrivers = 29;
            }
            else if (sectorSize < 256)
            {
                return(false);
            }

            AppleDriverDescriptorMap ddm = Marshal.ByteArrayToStructureBigEndian <AppleDriverDescriptorMap>(ddmSector);

            DicConsole.DebugWriteLine("AppleMap Plugin", "ddm.sbSig = 0x{0:X4}", ddm.sbSig);
            DicConsole.DebugWriteLine("AppleMap Plugin", "ddm.sbBlockSize = {0}", ddm.sbBlockSize);
            DicConsole.DebugWriteLine("AppleMap Plugin", "ddm.sbBlocks = {0}", ddm.sbBlocks);
            DicConsole.DebugWriteLine("AppleMap Plugin", "ddm.sbDevType = {0}", ddm.sbDevType);
            DicConsole.DebugWriteLine("AppleMap Plugin", "ddm.sbDevId = {0}", ddm.sbDevId);
            DicConsole.DebugWriteLine("AppleMap Plugin", "ddm.sbData = 0x{0:X8}", ddm.sbData);
            DicConsole.DebugWriteLine("AppleMap Plugin", "ddm.sbDrvrCount = {0}", ddm.sbDrvrCount);

            uint sequence = 0;

            if (ddm.sbSig == DDM_MAGIC)
            {
                if (ddm.sbDrvrCount < maxDrivers)
                {
                    ddm.sbMap = new AppleDriverEntry[ddm.sbDrvrCount];
                    for (int i = 0; i < ddm.sbDrvrCount; i++)
                    {
                        byte[] tmp = new byte[8];
                        Array.Copy(ddmSector, 18 + i * 8, tmp, 0, 8);
                        ddm.sbMap[i] = Marshal.ByteArrayToStructureBigEndian <AppleDriverEntry>(tmp);
                        DicConsole.DebugWriteLine("AppleMap Plugin", "ddm.sbMap[{1}].ddBlock = {0}",
                                                  ddm.sbMap[i].ddBlock, i);
                        DicConsole.DebugWriteLine("AppleMap Plugin", "ddm.sbMap[{1}].ddSize = {0}", ddm.sbMap[i].ddSize,
                                                  i);
                        DicConsole.DebugWriteLine("AppleMap Plugin", "ddm.sbMap[{1}].ddType = {0}", ddm.sbMap[i].ddType,
                                                  i);

                        if (ddm.sbMap[i].ddSize == 0)
                        {
                            continue;
                        }

                        Partition part = new Partition
                        {
                            Size     = (ulong)(ddm.sbMap[i].ddSize * 512),
                            Length   = (ulong)(ddm.sbMap[i].ddSize * 512 / sectorSize),
                            Sequence = sequence,
                            Offset   = ddm.sbMap[i].ddBlock * sectorSize,
                            Start    = ddm.sbMap[i].ddBlock + sectorOffset,
                            Type     = "Apple_Driver"
                        };

                        if (ddm.sbMap[i].ddSize * 512 % sectorSize > 0)
                        {
                            part.Length++;
                        }

                        partitions.Add(part);

                        sequence++;
                    }
                }
            }

            byte[] partSector = imagePlugin.ReadSector(1 + sectorOffset);
            AppleOldDevicePartitionMap oldMap =
                Marshal.ByteArrayToStructureBigEndian <AppleOldDevicePartitionMap>(partSector);

            // This is the easy one, no sector size mixing
            if (oldMap.pdSig == APM_MAGIC_OLD)
            {
                for (int i = 2; i < partSector.Length; i += 12)
                {
                    byte[] tmp = new byte[12];
                    Array.Copy(partSector, i, tmp, 0, 12);
                    AppleMapOldPartitionEntry oldEntry =
                        Marshal.ByteArrayToStructureBigEndian <AppleMapOldPartitionEntry>(tmp);
                    DicConsole.DebugWriteLine("AppleMap Plugin", "old_map.sbMap[{1}].pdStart = {0}", oldEntry.pdStart,
                                              (i - 2) / 12);
                    DicConsole.DebugWriteLine("AppleMap Plugin", "old_map.sbMap[{1}].pdSize = {0}", oldEntry.pdSize,
                                              (i - 2) / 12);
                    DicConsole.DebugWriteLine("AppleMap Plugin", "old_map.sbMap[{1}].pdFSID = 0x{0:X8}",
                                              oldEntry.pdFSID, (i - 2) / 12);

                    if (oldEntry.pdSize == 0 && oldEntry.pdFSID == 0)
                    {
                        if (oldEntry.pdStart == 0)
                        {
                            break;
                        }

                        continue;
                    }

                    Partition part = new Partition
                    {
                        Size     = oldEntry.pdStart * ddm.sbBlockSize,
                        Length   = oldEntry.pdStart * ddm.sbBlockSize / sectorSize,
                        Sequence = sequence,
                        Offset   = oldEntry.pdSize * ddm.sbBlockSize,
                        Start    = oldEntry.pdSize * ddm.sbBlockSize / sectorSize,
                        Scheme   = Name,
                        Type     = oldEntry.pdFSID == HFS_MAGIC_OLD ? "Apple_HFS" : $"0x{oldEntry.pdFSID:X8}"
                    };

                    partitions.Add(part);

                    sequence++;
                }

                return(partitions.Count > 0);
            }

            AppleMapPartitionEntry entry;
            uint entrySize;
            uint entryCount;
            uint sectorsToRead;
            uint skipDdm;

            // If sector is bigger than 512
            if (ddmSector.Length > 512)
            {
                byte[] tmp = new byte[512];
                Array.Copy(ddmSector, 512, tmp, 0, 512);
                entry = Marshal.ByteArrayToStructureBigEndian <AppleMapPartitionEntry>(tmp);
                // Check for a partition entry that's 512-byte aligned
                if (entry.signature == APM_MAGIC)
                {
                    DicConsole.DebugWriteLine("AppleMap Plugin", "Found misaligned entry.");
                    entrySize     = 512;
                    entryCount    = entry.entries;
                    skipDdm       = 512;
                    sectorsToRead = (entryCount + 1) * 512 / sectorSize + 1;
                }
                else
                {
                    entry = Marshal.ByteArrayToStructureBigEndian <AppleMapPartitionEntry>(partSector);
                    if (entry.signature == APM_MAGIC)
                    {
                        DicConsole.DebugWriteLine("AppleMap Plugin", "Found aligned entry.");
                        entrySize     = sectorSize;
                        entryCount    = entry.entries;
                        skipDdm       = sectorSize;
                        sectorsToRead = entryCount + 2;
                    }
                    else
                    {
                        return(partitions.Count > 0);
                    }
                }
            }
            else
            {
                entry = Marshal.ByteArrayToStructureBigEndian <AppleMapPartitionEntry>(partSector);
                if (entry.signature == APM_MAGIC)
                {
                    DicConsole.DebugWriteLine("AppleMap Plugin", "Found aligned entry.");
                    entrySize     = sectorSize;
                    entryCount    = entry.entries;
                    skipDdm       = sectorSize;
                    sectorsToRead = entryCount + 2;
                }
                else
                {
                    return(partitions.Count > 0);
                }
            }

            byte[] entries = imagePlugin.ReadSectors(sectorOffset, sectorsToRead);
            DicConsole.DebugWriteLine("AppleMap Plugin", "entry_size = {0}", entrySize);
            DicConsole.DebugWriteLine("AppleMap Plugin", "entry_count = {0}", entryCount);
            DicConsole.DebugWriteLine("AppleMap Plugin", "skip_ddm = {0}", skipDdm);
            DicConsole.DebugWriteLine("AppleMap Plugin", "sectors_to_read = {0}", sectorsToRead);

            byte[] copy = new byte[entries.Length - skipDdm];
            Array.Copy(entries, skipDdm, copy, 0, copy.Length);
            entries = copy;

            for (int i = 0; i < entryCount; i++)
            {
                byte[] tmp = new byte[entrySize];
                Array.Copy(entries, i * entrySize, tmp, 0, entrySize);
                entry = Marshal.ByteArrayToStructureBigEndian <AppleMapPartitionEntry>(tmp);
                if (entry.signature != APM_MAGIC)
                {
                    continue;
                }

                DicConsole.DebugWriteLine("AppleMap Plugin", "dpme[{0}].signature = 0x{1:X4}", i, entry.signature);
                DicConsole.DebugWriteLine("AppleMap Plugin", "dpme[{0}].reserved1 = 0x{1:X4}", i, entry.reserved1);
                DicConsole.DebugWriteLine("AppleMap Plugin", "dpme[{0}].entries = {1}", i, entry.entries);
                DicConsole.DebugWriteLine("AppleMap Plugin", "dpme[{0}].start = {1}", i, entry.start);
                DicConsole.DebugWriteLine("AppleMap Plugin", "dpme[{0}].sectors = {1}", i, entry.sectors);
                DicConsole.DebugWriteLine("AppleMap Plugin", "dpme[{0}].name = \"{1}\"", i,
                                          StringHandlers.CToString(entry.name));
                DicConsole.DebugWriteLine("AppleMap Plugin", "dpme[{0}].type = \"{1}\"", i,
                                          StringHandlers.CToString(entry.type));
                DicConsole.DebugWriteLine("AppleMap Plugin", "dpme[{0}].first_data_block = {1}", i,
                                          entry.first_data_block);
                DicConsole.DebugWriteLine("AppleMap Plugin", "dpme[{0}].data_sectors = {1}", i, entry.data_sectors);
                DicConsole.DebugWriteLine("AppleMap Plugin", "dpme[{0}].flags = {1}", i,
                                          (AppleMapFlags)entry.flags);
                DicConsole.DebugWriteLine("AppleMap Plugin", "dpme[{0}].first_boot_block = {1}", i,
                                          entry.first_boot_block);
                DicConsole.DebugWriteLine("AppleMap Plugin", "dpme[{0}].boot_size = {1}", i, entry.boot_size);
                DicConsole.DebugWriteLine("AppleMap Plugin", "dpme[{0}].load_address = 0x{1:X8}", i,
                                          entry.load_address);
                DicConsole.DebugWriteLine("AppleMap Plugin", "dpme[{0}].load_address2 = 0x{1:X8}", i,
                                          entry.load_address2);
                DicConsole.DebugWriteLine("AppleMap Plugin", "dpme[{0}].entry_point = 0x{1:X8}", i, entry.entry_point);
                DicConsole.DebugWriteLine("AppleMap Plugin", "dpme[{0}].entry_point2 = 0x{1:X8}", i,
                                          entry.entry_point2);
                DicConsole.DebugWriteLine("AppleMap Plugin", "dpme[{0}].checksum = 0x{1:X8}", i, entry.checksum);
                DicConsole.DebugWriteLine("AppleMap Plugin", "dpme[{0}].processor = \"{1}\"", i,
                                          StringHandlers.CToString(entry.processor));

                AppleMapFlags flags = (AppleMapFlags)entry.flags;

                // BeOS doesn't mark its partitions as valid
                //if(flags.HasFlag(AppleMapFlags.Valid) &&
                if (StringHandlers.CToString(entry.type) == "Apple_partition_map" || entry.sectors <= 0)
                {
                    continue;
                }

                StringBuilder sb = new StringBuilder();

                Partition partition = new Partition
                {
                    Sequence = sequence,
                    Type     = StringHandlers.CToString(entry.type),
                    Name     = StringHandlers.CToString(entry.name),
                    Offset   = entry.start * entrySize,
                    Size     = entry.sectors * entrySize,
                    Start    = entry.start * entrySize / sectorSize + sectorOffset,
                    Length   = entry.sectors * entrySize / sectorSize,
                    Scheme   = Name
                };
                sb.AppendLine("Partition flags:");
                if (flags.HasFlag(AppleMapFlags.Valid))
                {
                    sb.AppendLine("Partition is valid.");
                }
                if (flags.HasFlag(AppleMapFlags.Allocated))
                {
                    sb.AppendLine("Partition entry is allocated.");
                }
                if (flags.HasFlag(AppleMapFlags.InUse))
                {
                    sb.AppendLine("Partition is in use.");
                }
                if (flags.HasFlag(AppleMapFlags.Bootable))
                {
                    sb.AppendLine("Partition is bootable.");
                }
                if (flags.HasFlag(AppleMapFlags.Readable))
                {
                    sb.AppendLine("Partition is readable.");
                }
                if (flags.HasFlag(AppleMapFlags.Writable))
                {
                    sb.AppendLine("Partition is writable.");
                }

                if (flags.HasFlag(AppleMapFlags.Bootable))
                {
                    sb.AppendFormat("First boot sector: {0}", entry.first_boot_block * entrySize / sectorSize)
                    .AppendLine();
                    sb.AppendFormat("Boot is {0} bytes.", entry.boot_size).AppendLine();
                    sb.AppendFormat("Boot load address: 0x{0:X8}", entry.load_address).AppendLine();
                    sb.AppendFormat("Boot entry point: 0x{0:X8}", entry.entry_point).AppendLine();
                    sb.AppendFormat("Boot code checksum: 0x{0:X8}", entry.checksum).AppendLine();
                    sb.AppendFormat("Processor: {0}", StringHandlers.CToString(entry.processor)).AppendLine();

                    if (flags.HasFlag(AppleMapFlags.PicCode))
                    {
                        sb.AppendLine("Partition's boot code is position independent.");
                    }
                }

                partition.Description = sb.ToString();
                if (partition.Start < imagePlugin.Info.Sectors && partition.End < imagePlugin.Info.Sectors)
                {
                    partitions.Add(partition);
                    sequence++;
                }
                // Some CD and DVDs end with an Apple_Free that expands beyond the disc size...
                else if (partition.Start < imagePlugin.Info.Sectors)
                {
                    DicConsole.DebugWriteLine("AppleMap Plugin", "Cutting last partition end ({0}) to media size ({1})",
                                              partition.End, imagePlugin.Info.Sectors - 1);
                    partition.Length = imagePlugin.Info.Sectors - partition.Start;
                    partitions.Add(partition);
                    sequence++;
                }
                else
                {
                    DicConsole.DebugWriteLine("AppleMap Plugin",
                                              "Not adding partition becaus start ({0}) is outside media size ({1})",
                                              partition.Start, imagePlugin.Info.Sectors - 1);
                }
            }

            return(partitions.Count > 0);
        }
Пример #10
0
        public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information,
                                   Encoding encoding)
        {
            Encoding    = encoding ?? Encoding.ASCII;
            information = "";
            var isoMetadata = new StringBuilder();

            byte[] vdMagic = new byte[5];     // Volume Descriptor magic "CD001"
            byte[] hsMagic = new byte[5];     // Volume Descriptor magic "CDROM"

            string bootSpec = "";

            PrimaryVolumeDescriptor?pvd      = null;
            PrimaryVolumeDescriptor?jolietvd = null;
            BootRecord?bvd = null;
            HighSierraPrimaryVolumeDescriptor?hsvd = null;
            FileStructureVolumeDescriptor?    fsvd = null;
            ElToritoBootRecord?torito = null;

            // ISO9660 is designed for 2048 bytes/sector devices
            if (imagePlugin.Info.SectorSize < 2048)
            {
                return;
            }

            // ISO9660 Primary Volume Descriptor starts at sector 16, so that's minimal size.
            if (partition.End < 16)
            {
                return;
            }

            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);
            bool highSierraInfo = Encoding.GetString(hsMagic) == HIGH_SIERRA_MAGIC;
            int  hsOff          = 0;

            if (highSierraInfo)
            {
                hsOff = 8;
            }

            bool cdiInfo = false;
            bool evd     = false;
            bool vpd     = false;

            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;
                    }

                    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;
                    }

                    break;
                }

                cdiInfo |= Encoding.GetString(vdMagic) == CDI_MAGIC;

                switch (vdType)
                {
                case 0:
                {
                    bvd = Marshal.ByteArrayToStructureLittleEndian <BootRecord>(vdSector, hsOff, 2048 - hsOff);

                    bootSpec = "Unknown";

                    if (Encoding.GetString(bvd.Value.system_id).Substring(0, 23) == "EL TORITO SPECIFICATION")
                    {
                        bootSpec = "El Torito";

                        torito =
                            Marshal.ByteArrayToStructureLittleEndian <ElToritoBootRecord>(vdSector, hsOff,
                                                                                          2048 - hsOff);
                    }

                    break;
                }

                case 1:
                {
                    if (highSierraInfo)
                    {
                        hsvd = Marshal.
                               ByteArrayToStructureLittleEndian <HighSierraPrimaryVolumeDescriptor>(vdSector);
                    }
                    else if (cdiInfo)
                    {
                        fsvd = Marshal.ByteArrayToStructureBigEndian <FileStructureVolumeDescriptor>(vdSector);
                    }
                    else
                    {
                        pvd = Marshal.ByteArrayToStructureLittleEndian <PrimaryVolumeDescriptor>(vdSector);
                    }

                    break;
                }

                case 2:
                {
                    PrimaryVolumeDescriptor svd =
                        Marshal.ByteArrayToStructureLittleEndian <PrimaryVolumeDescriptor>(vdSector);

                    // 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.WriteLine("ISO9660 plugin",
                                                      "Found unknown supplementary volume descriptor");
                            }
                        }
                    }
                    else
                    {
                        evd = true;
                    }

                    break;
                }

                case 3:
                {
                    vpd = true;

                    break;
                }
                }

                counter++;
            }

            DecodedVolumeDescriptor decodedVd;
            var decodedJolietVd = new DecodedVolumeDescriptor();

            XmlFsType = new FileSystemType();

            if (pvd == null &&
                hsvd == null &&
                fsvd == null)
            {
                information = "ERROR: Could not find primary volume descriptor";

                return;
            }

            if (highSierraInfo)
            {
                decodedVd = DecodeVolumeDescriptor(hsvd.Value);
            }
            else if (cdiInfo)
            {
                decodedVd = DecodeVolumeDescriptor(fsvd.Value);
            }
            else
            {
                decodedVd = DecodeVolumeDescriptor(pvd.Value);
            }

            if (jolietvd != null)
            {
                decodedJolietVd = DecodeJolietDescriptor(jolietvd.Value);
            }

            uint rootLocation = 0;
            uint rootSize     = 0;

            // No need to read root on CD-i, as extensions are not supported...
            if (!cdiInfo)
            {
                rootLocation = highSierraInfo ? hsvd.Value.root_directory_record.extent
                                   : pvd.Value.root_directory_record.extent;

                if (highSierraInfo)
                {
                    rootSize = hsvd.Value.root_directory_record.size / hsvd.Value.logical_block_size;

                    if (hsvd.Value.root_directory_record.size % hsvd.Value.logical_block_size > 0)
                    {
                        rootSize++;
                    }
                }
                else
                {
                    rootSize = pvd.Value.root_directory_record.size / pvd.Value.logical_block_size;

                    if (pvd.Value.root_directory_record.size % pvd.Value.logical_block_size > 0)
                    {
                        rootSize++;
                    }
                }
            }

            byte[] rootDir      = new byte[0];
            int    rootOff      = 0;
            bool   xaExtensions = false;
            bool   apple        = false;
            bool   susp         = false;
            bool   rrip         = false;
            bool   ziso         = false;
            bool   amiga        = false;
            bool   aaip         = false;
            List <ContinuationArea> contareas = new List <ContinuationArea>();
            List <byte[]>           refareas  = new List <byte[]>();
            var suspInformation = new StringBuilder();

            if (rootLocation + rootSize < imagePlugin.Info.Sectors)
            {
                rootDir = imagePlugin.ReadSectors(rootLocation, rootSize);
            }

            // Walk thru root directory to see system area extensions in use
            while (rootOff + Marshal.SizeOf <DirectoryRecord>() < rootDir.Length &&
                   !cdiInfo)
            {
                DirectoryRecord record =
                    Marshal.ByteArrayToStructureLittleEndian <DirectoryRecord>(rootDir, rootOff,
                                                                               Marshal.SizeOf <DirectoryRecord>());

                int saOff = Marshal.SizeOf <DirectoryRecord>() + record.name_len;
                saOff += saOff % 2;
                int saLen = record.length - saOff;

                if (saLen > 0 &&
                    rootOff + saOff + saLen <= rootDir.Length)
                {
                    byte[] sa = new byte[saLen];
                    Array.Copy(rootDir, rootOff + saOff, sa, 0, saLen);
                    saOff = 0;

                    while (saOff < saLen)
                    {
                        bool noneFound = true;

                        if (Marshal.SizeOf <CdromXa>() + saOff <= saLen)
                        {
                            CdromXa xa = Marshal.ByteArrayToStructureBigEndian <CdromXa>(sa);

                            if (xa.signature == XA_MAGIC)
                            {
                                xaExtensions = true;
                                saOff       += Marshal.SizeOf <CdromXa>();
                                noneFound    = false;
                            }
                        }

                        if (saOff + 2 >= saLen)
                        {
                            break;
                        }

                        ushort nextSignature = BigEndianBitConverter.ToUInt16(sa, saOff);

                        switch (nextSignature)
                        {
                        // Easy, contains size field
                        case APPLE_MAGIC:
                            apple     = true;
                            saOff    += sa[saOff + 2];
                            noneFound = false;

                            break;

                        // Not easy, contains size field
                        case APPLE_MAGIC_OLD:
                            apple = true;
                            var appleId = (AppleOldId)sa[saOff + 2];
                            noneFound = false;

                            switch (appleId)
                            {
                            case AppleOldId.ProDOS:
                                saOff += Marshal.SizeOf <AppleProDOSOldSystemUse>();

                                break;

                            case AppleOldId.TypeCreator:
                            case AppleOldId.TypeCreatorBundle:
                                saOff += Marshal.SizeOf <AppleHFSTypeCreatorSystemUse>();

                                break;

                            case AppleOldId.TypeCreatorIcon:
                            case AppleOldId.TypeCreatorIconBundle:
                                saOff += Marshal.SizeOf <AppleHFSIconSystemUse>();

                                break;

                            case AppleOldId.HFS:
                                saOff += Marshal.SizeOf <AppleHFSOldSystemUse>();

                                break;
                            }

                            break;

                        // IEEE-P1281 aka SUSP 1.12
                        case SUSP_INDICATOR:
                            susp      = true;
                            saOff    += sa[saOff + 2];
                            noneFound = false;

                            while (saOff + 2 < saLen)
                            {
                                nextSignature = BigEndianBitConverter.ToUInt16(sa, saOff);

                                switch (nextSignature)
                                {
                                case APPLE_MAGIC:
                                    if (sa[saOff + 3] == 1 &&
                                        sa[saOff + 2] == 7)
                                    {
                                        apple = true;
                                    }
                                    else
                                    {
                                        apple |= sa[saOff + 3] != 1;
                                    }

                                    break;

                                case SUSP_CONTINUATION when saOff + sa[saOff + 2] <= saLen:
                                    byte[] ce = new byte[sa[saOff + 2]];
                                    Array.Copy(sa, saOff, ce, 0, ce.Length);

                                    ContinuationArea ca =
                                        Marshal.ByteArrayToStructureBigEndian <ContinuationArea>(ce);

                                    contareas.Add(ca);

                                    break;

                                case SUSP_REFERENCE when saOff + sa[saOff + 2] <= saLen:
                                    byte[] er = new byte[sa[saOff + 2]];
                                    Array.Copy(sa, saOff, er, 0, er.Length);
                                    refareas.Add(er);

                                    break;
                                }

                                rrip |= nextSignature == RRIP_MAGIC ||
                                        nextSignature == RRIP_POSIX_ATTRIBUTES ||
                                        nextSignature == RRIP_POSIX_DEV_NO ||
                                        nextSignature == RRIP_SYMLINK ||
                                        nextSignature == RRIP_NAME ||
                                        nextSignature == RRIP_CHILDLINK ||
                                        nextSignature == RRIP_PARENTLINK ||
                                        nextSignature == RRIP_RELOCATED_DIR ||
                                        nextSignature == RRIP_TIMESTAMPS || nextSignature == RRIP_SPARSE;

                                ziso  |= nextSignature == ZISO_MAGIC;
                                amiga |= nextSignature == AMIGA_MAGIC;

                                aaip |= nextSignature == AAIP_MAGIC || (nextSignature == AAIP_MAGIC_OLD &&
                                                                        sa[saOff + 3] == 1 &&
                                                                        sa[saOff + 2] >= 9);

                                saOff += sa[saOff + 2];

                                if (nextSignature == SUSP_TERMINATOR)
                                {
                                    break;
                                }
                            }

                            break;
                        }

                        if (noneFound)
                        {
                            break;
                        }
                    }
                }

                rootOff += record.length;

                if (record.length == 0)
                {
                    break;
                }
            }

            foreach (ContinuationArea ca in contareas)
            {
                uint caLen = (ca.ca_length_be + ca.offset_be) /
                             (highSierraInfo ? hsvd.Value.logical_block_size : pvd.Value.logical_block_size);

                if ((ca.ca_length_be + ca.offset_be) %
                    (highSierraInfo ? hsvd.Value.logical_block_size : pvd.Value.logical_block_size) > 0)
                {
                    caLen++;
                }

                byte[] caSectors = imagePlugin.ReadSectors(ca.block_be, caLen);
                byte[] caData    = new byte[ca.ca_length_be];
                Array.Copy(caSectors, ca.offset_be, caData, 0, ca.ca_length_be);
                int caOff = 0;

                while (caOff < ca.ca_length_be)
                {
                    ushort nextSignature = BigEndianBitConverter.ToUInt16(caData, caOff);

                    switch (nextSignature)
                    {
                    // Apple never said to include its extensions inside a continuation area, but just in case
                    case APPLE_MAGIC:
                        if (caData[caOff + 3] == 1 &&
                            caData[caOff + 2] == 7)
                        {
                            apple = true;
                        }
                        else
                        {
                            apple |= caData[caOff + 3] != 1;
                        }

                        break;

                    case SUSP_REFERENCE when caOff + caData[caOff + 2] <= ca.ca_length_be:
                        byte[] er = new byte[caData[caOff + 2]];
                        Array.Copy(caData, caOff, er, 0, er.Length);
                        refareas.Add(er);

                        break;
                    }

                    rrip |= nextSignature == RRIP_MAGIC || nextSignature == RRIP_POSIX_ATTRIBUTES ||
                            nextSignature == RRIP_POSIX_DEV_NO || nextSignature == RRIP_SYMLINK ||
                            nextSignature == RRIP_NAME || nextSignature == RRIP_CHILDLINK ||
                            nextSignature == RRIP_PARENTLINK || nextSignature == RRIP_RELOCATED_DIR ||
                            nextSignature == RRIP_TIMESTAMPS || nextSignature == RRIP_SPARSE;

                    ziso  |= nextSignature == ZISO_MAGIC;
                    amiga |= nextSignature == AMIGA_MAGIC;

                    aaip |= nextSignature == AAIP_MAGIC || (nextSignature == AAIP_MAGIC_OLD && caData[caOff + 3] == 1 &&
                                                            caData[caOff + 2] >= 9);

                    caOff += caData[caOff + 2];
                }
            }

            if (refareas.Count > 0)
            {
                suspInformation.AppendLine("----------------------------------------");
                suspInformation.AppendLine("SYSTEM USE SHARING PROTOCOL INFORMATION:");
                suspInformation.AppendLine("----------------------------------------");

                counter = 1;

                foreach (byte[] erb in refareas)
                {
                    ReferenceArea er    = Marshal.ByteArrayToStructureBigEndian <ReferenceArea>(erb);
                    string        extId = Encoding.GetString(erb, Marshal.SizeOf <ReferenceArea>(), er.id_len);

                    string extDes = Encoding.GetString(erb, Marshal.SizeOf <ReferenceArea>() + er.id_len, er.des_len);

                    string extSrc = Encoding.GetString(erb, Marshal.SizeOf <ReferenceArea>() + er.id_len + er.des_len,
                                                       er.src_len);

                    suspInformation.AppendFormat("Extension: {0}", counter).AppendLine();
                    suspInformation.AppendFormat("\tID: {0}, version {1}", extId, er.ext_ver).AppendLine();
                    suspInformation.AppendFormat("\tDescription: {0}", extDes).AppendLine();
                    suspInformation.AppendFormat("\tSource: {0}", extSrc).AppendLine();
                    counter++;
                }
            }

            byte[]          ipbinSector = imagePlugin.ReadSector(0 + partition.Start);
            CD.IPBin?       segaCd      = CD.DecodeIPBin(ipbinSector);
            Saturn.IPBin?   saturn      = Saturn.DecodeIPBin(ipbinSector);
            Dreamcast.IPBin?dreamcast   = Dreamcast.DecodeIPBin(ipbinSector);

            string fsFormat;

            if (highSierraInfo)
            {
                fsFormat = "High Sierra Format";
            }
            else if (cdiInfo)
            {
                fsFormat = "CD-i";
            }
            else
            {
                fsFormat = "ISO9660";
            }

            isoMetadata.AppendFormat("{0} file system", fsFormat).AppendLine();

            if (xaExtensions)
            {
                isoMetadata.AppendLine("CD-ROM XA extensions present.");
            }

            if (amiga)
            {
                isoMetadata.AppendLine("Amiga extensions present.");
            }

            if (apple)
            {
                isoMetadata.AppendLine("Apple extensions present.");
            }

            if (jolietvd != null)
            {
                isoMetadata.AppendLine("Joliet extensions present.");
            }

            if (susp)
            {
                isoMetadata.AppendLine("System Use Sharing Protocol present.");
            }

            if (rrip)
            {
                isoMetadata.AppendLine("Rock Ridge Interchange Protocol present.");
            }

            if (aaip)
            {
                isoMetadata.AppendLine("Arbitrary Attribute Interchange Protocol present.");
            }

            if (ziso)
            {
                isoMetadata.AppendLine("zisofs compression present.");
            }

            if (evd)
            {
                isoMetadata.AppendLine("Contains Enhanved Volume Descriptor.");
            }

            if (vpd)
            {
                isoMetadata.AppendLine("Contains Volume Partition Descriptor.");
            }

            if (bvd != null)
            {
                isoMetadata.AppendFormat("Disc bootable following {0} specifications.", bootSpec).AppendLine();
            }

            if (segaCd != null)
            {
                isoMetadata.AppendLine("This is a SegaCD / MegaCD disc.");
                isoMetadata.AppendLine(CD.Prettify(segaCd));
            }

            if (saturn != null)
            {
                isoMetadata.AppendLine("This is a Sega Saturn disc.");
                isoMetadata.AppendLine(Saturn.Prettify(saturn));
            }

            if (dreamcast != null)
            {
                isoMetadata.AppendLine("This is a Sega Dreamcast disc.");
                isoMetadata.AppendLine(Dreamcast.Prettify(dreamcast));
            }

            isoMetadata.AppendFormat("{0}------------------------------", cdiInfo ? "---------------" : "").
            AppendLine();

            isoMetadata.AppendFormat("{0}VOLUME DESCRIPTOR INFORMATION:", cdiInfo ? "FILE STRUCTURE " : "").
            AppendLine();

            isoMetadata.AppendFormat("{0}------------------------------", cdiInfo ? "---------------" : "").
            AppendLine();

            isoMetadata.AppendFormat("System identifier: {0}", decodedVd.SystemIdentifier).AppendLine();
            isoMetadata.AppendFormat("Volume identifier: {0}", decodedVd.VolumeIdentifier).AppendLine();
            isoMetadata.AppendFormat("Volume set identifier: {0}", decodedVd.VolumeSetIdentifier).AppendLine();
            isoMetadata.AppendFormat("Publisher identifier: {0}", decodedVd.PublisherIdentifier).AppendLine();
            isoMetadata.AppendFormat("Data preparer identifier: {0}", decodedVd.DataPreparerIdentifier).AppendLine();
            isoMetadata.AppendFormat("Application identifier: {0}", decodedVd.ApplicationIdentifier).AppendLine();
            isoMetadata.AppendFormat("Volume creation date: {0}", decodedVd.CreationTime).AppendLine();

            if (decodedVd.HasModificationTime)
            {
                isoMetadata.AppendFormat("Volume modification date: {0}", decodedVd.ModificationTime).AppendLine();
            }
            else
            {
                isoMetadata.AppendFormat("Volume has not been modified.").AppendLine();
            }

            if (decodedVd.HasExpirationTime)
            {
                isoMetadata.AppendFormat("Volume expiration date: {0}", decodedVd.ExpirationTime).AppendLine();
            }
            else
            {
                isoMetadata.AppendFormat("Volume does not expire.").AppendLine();
            }

            if (decodedVd.HasEffectiveTime)
            {
                isoMetadata.AppendFormat("Volume effective date: {0}", decodedVd.EffectiveTime).AppendLine();
            }
            else
            {
                isoMetadata.AppendFormat("Volume has always been effective.").AppendLine();
            }

            isoMetadata.AppendFormat("Volume has {0} blocks of {1} bytes each", decodedVd.Blocks, decodedVd.BlockSize).
            AppendLine();

            if (jolietvd != null)
            {
                isoMetadata.AppendLine("-------------------------------------");
                isoMetadata.AppendLine("JOLIET VOLUME DESCRIPTOR INFORMATION:");
                isoMetadata.AppendLine("-------------------------------------");
                isoMetadata.AppendFormat("System identifier: {0}", decodedJolietVd.SystemIdentifier).AppendLine();
                isoMetadata.AppendFormat("Volume identifier: {0}", decodedJolietVd.VolumeIdentifier).AppendLine();

                isoMetadata.AppendFormat("Volume set identifier: {0}", decodedJolietVd.VolumeSetIdentifier).
                AppendLine();

                isoMetadata.AppendFormat("Publisher identifier: {0}", decodedJolietVd.PublisherIdentifier).AppendLine();

                isoMetadata.AppendFormat("Data preparer identifier: {0}", decodedJolietVd.DataPreparerIdentifier).
                AppendLine();

                isoMetadata.AppendFormat("Application identifier: {0}", decodedJolietVd.ApplicationIdentifier).
                AppendLine();

                isoMetadata.AppendFormat("Volume creation date: {0}", decodedJolietVd.CreationTime).AppendLine();

                if (decodedJolietVd.HasModificationTime)
                {
                    isoMetadata.AppendFormat("Volume modification date: {0}", decodedJolietVd.ModificationTime).
                    AppendLine();
                }
                else
                {
                    isoMetadata.AppendFormat("Volume has not been modified.").AppendLine();
                }

                if (decodedJolietVd.HasExpirationTime)
                {
                    isoMetadata.AppendFormat("Volume expiration date: {0}", decodedJolietVd.ExpirationTime).
                    AppendLine();
                }
                else
                {
                    isoMetadata.AppendFormat("Volume does not expire.").AppendLine();
                }

                if (decodedJolietVd.HasEffectiveTime)
                {
                    isoMetadata.AppendFormat("Volume effective date: {0}", decodedJolietVd.EffectiveTime).AppendLine();
                }
                else
                {
                    isoMetadata.AppendFormat("Volume has always been effective.").AppendLine();
                }
            }

            if (torito != null)
            {
                vdSector = imagePlugin.ReadSector(torito.Value.catalog_sector + partition.Start);

                int toritoOff = 0;

                if (vdSector[toritoOff] != 1)
                {
                    goto exit_torito;
                }

                ElToritoValidationEntry valentry =
                    Marshal.ByteArrayToStructureLittleEndian <ElToritoValidationEntry>(vdSector, toritoOff,
                                                                                       EL_TORITO_ENTRY_SIZE);

                if (valentry.signature != EL_TORITO_MAGIC)
                {
                    goto exit_torito;
                }

                toritoOff += EL_TORITO_ENTRY_SIZE;

                ElToritoInitialEntry initialEntry =
                    Marshal.ByteArrayToStructureLittleEndian <ElToritoInitialEntry>(vdSector, toritoOff,
                                                                                    EL_TORITO_ENTRY_SIZE);

                initialEntry.boot_type = (ElToritoEmulation)((byte)initialEntry.boot_type & 0xF);

                AaruConsole.DebugWriteLine("DEBUG (ISO9660 plugin)", "initialEntry.load_rba = {0}",
                                           initialEntry.load_rba);

                AaruConsole.DebugWriteLine("DEBUG (ISO9660 plugin)", "initialEntry.sector_count = {0}",
                                           initialEntry.sector_count);

                byte[] bootImage =
                    (initialEntry.load_rba + partition.Start + initialEntry.sector_count) - 1 <=
                    partition.End
                        ? imagePlugin.ReadSectors(initialEntry.load_rba + partition.Start, initialEntry.sector_count)
                        : null;

                isoMetadata.AppendLine("----------------------");
                isoMetadata.AppendLine("EL TORITO INFORMATION:");
                isoMetadata.AppendLine("----------------------");

                isoMetadata.AppendLine("Initial entry:");
                isoMetadata.AppendFormat("\tDeveloper ID: {0}", Encoding.GetString(valentry.developer_id)).AppendLine();

                if (initialEntry.bootable == ElToritoIndicator.Bootable)
                {
                    isoMetadata.AppendFormat("\tBootable on {0}", valentry.platform_id).AppendLine();

                    isoMetadata.AppendFormat("\tBootable image starts at sector {0} and runs for {1} sectors",
                                             initialEntry.load_rba, initialEntry.sector_count).AppendLine();

                    if (valentry.platform_id == ElToritoPlatform.x86)
                    {
                        isoMetadata.AppendFormat("\tBootable image will be loaded at segment {0:X4}h",
                                                 initialEntry.load_seg == 0 ? 0x7C0 : initialEntry.load_seg).
                        AppendLine();
                    }
                    else
                    {
                        isoMetadata.AppendFormat("\tBootable image will be loaded at 0x{0:X8}",
                                                 (uint)initialEntry.load_seg * 10).AppendLine();
                    }

                    switch (initialEntry.boot_type)
                    {
                    case ElToritoEmulation.None:
                        isoMetadata.AppendLine("\tImage uses no emulation");

                        break;

                    case ElToritoEmulation.Md2hd:
                        isoMetadata.AppendLine("\tImage emulates a 5.25\" high-density (MD2HD, 1.2Mb) floppy");

                        break;

                    case ElToritoEmulation.Mf2hd:
                        isoMetadata.AppendLine("\tImage emulates a 3.5\" high-density (MF2HD, 1.44Mb) floppy");

                        break;

                    case ElToritoEmulation.Mf2ed:
                        isoMetadata.AppendLine("\tImage emulates a 3.5\" extra-density (MF2ED, 2.88Mb) floppy");

                        break;

                    default:
                        isoMetadata.AppendFormat("\tImage uses unknown emulation type {0}",
                                                 (byte)initialEntry.boot_type).AppendLine();

                        break;
                    }

                    isoMetadata.AppendFormat("\tSystem type: 0x{0:X2}", initialEntry.system_type).AppendLine();

                    if (bootImage != null)
                    {
                        isoMetadata.AppendFormat("\tBootable image's SHA1: {0}", Sha1Context.Data(bootImage, out _)).
                        AppendLine();
                    }
                }
                else
                {
                    isoMetadata.AppendLine("\tNot bootable");
                }

                toritoOff += EL_TORITO_ENTRY_SIZE;

                const int SECTION_COUNTER = 2;

                while (toritoOff < vdSector.Length &&
                       (vdSector[toritoOff] == (byte)ElToritoIndicator.Header ||
                        vdSector[toritoOff] == (byte)ElToritoIndicator.LastHeader))
                {
                    ElToritoSectionHeaderEntry sectionHeader =
                        Marshal.ByteArrayToStructureLittleEndian <ElToritoSectionHeaderEntry>(vdSector, toritoOff,
                                                                                              EL_TORITO_ENTRY_SIZE);

                    toritoOff += EL_TORITO_ENTRY_SIZE;

                    isoMetadata.AppendFormat("Boot section {0}:", SECTION_COUNTER);

                    isoMetadata.AppendFormat("\tSection ID: {0}", Encoding.GetString(sectionHeader.identifier)).
                    AppendLine();

                    for (int entryCounter = 1; entryCounter <= sectionHeader.entries && toritoOff < vdSector.Length;
                         entryCounter++)
                    {
                        ElToritoSectionEntry sectionEntry =
                            Marshal.ByteArrayToStructureLittleEndian <ElToritoSectionEntry>(vdSector, toritoOff,
                                                                                            EL_TORITO_ENTRY_SIZE);

                        toritoOff += EL_TORITO_ENTRY_SIZE;

                        isoMetadata.AppendFormat("\tEntry {0}:", entryCounter);

                        if (sectionEntry.bootable == ElToritoIndicator.Bootable)
                        {
                            bootImage =
                                (sectionEntry.load_rba + partition.Start + sectionEntry.sector_count) - 1 <=
                                partition.End
                                    ? imagePlugin.ReadSectors(sectionEntry.load_rba + partition.Start,
                                                              sectionEntry.sector_count) : null;

                            isoMetadata.AppendFormat("\t\tBootable on {0}", sectionHeader.platform_id).AppendLine();

                            isoMetadata.AppendFormat("\t\tBootable image starts at sector {0} and runs for {1} sectors",
                                                     sectionEntry.load_rba, sectionEntry.sector_count).AppendLine();

                            if (valentry.platform_id == ElToritoPlatform.x86)
                            {
                                isoMetadata.AppendFormat("\t\tBootable image will be loaded at segment {0:X4}h",
                                                         sectionEntry.load_seg == 0 ? 0x7C0 : sectionEntry.load_seg).
                                AppendLine();
                            }
                            else
                            {
                                isoMetadata.AppendFormat("\t\tBootable image will be loaded at 0x{0:X8}",
                                                         (uint)sectionEntry.load_seg * 10).AppendLine();
                            }

                            switch ((ElToritoEmulation)((byte)sectionEntry.boot_type & 0xF))
                            {
                            case ElToritoEmulation.None:
                                isoMetadata.AppendLine("\t\tImage uses no emulation");

                                break;

                            case ElToritoEmulation.Md2hd:
                                isoMetadata.
                                AppendLine("\t\tImage emulates a 5.25\" high-density (MD2HD, 1.2Mb) floppy");

                                break;

                            case ElToritoEmulation.Mf2hd:
                                isoMetadata.
                                AppendLine("\t\tImage emulates a 3.5\" high-density (MF2HD, 1.44Mb) floppy");

                                break;

                            case ElToritoEmulation.Mf2ed:
                                isoMetadata.
                                AppendLine("\t\tImage emulates a 3.5\" extra-density (MF2ED, 2.88Mb) floppy");

                                break;

                            default:
                                isoMetadata.AppendFormat("\t\tImage uses unknown emulation type {0}",
                                                         (byte)initialEntry.boot_type).AppendLine();

                                break;
                            }

                            isoMetadata.AppendFormat("\t\tSelection criteria type: {0}",
                                                     sectionEntry.selection_criteria_type).AppendLine();

                            isoMetadata.AppendFormat("\t\tSystem type: 0x{0:X2}", sectionEntry.system_type).
                            AppendLine();

                            if (bootImage != null)
                            {
                                isoMetadata.AppendFormat("\t\tBootable image's SHA1: {0}",
                                                         Sha1Context.Data(bootImage, out _)).AppendLine();
                            }
                        }
                        else
                        {
                            isoMetadata.AppendLine("\t\tNot bootable");
                        }

                        var flags = (ElToritoFlags)((byte)sectionEntry.boot_type & 0xF0);

                        if (flags.HasFlag(ElToritoFlags.ATAPI))
                        {
                            isoMetadata.AppendLine("\t\tImage contains ATAPI drivers");
                        }

                        if (flags.HasFlag(ElToritoFlags.SCSI))
                        {
                            isoMetadata.AppendLine("\t\tImage contains SCSI drivers");
                        }

                        if (!flags.HasFlag(ElToritoFlags.Continued))
                        {
                            continue;
                        }

                        while (toritoOff < vdSector.Length)
                        {
                            ElToritoSectionEntryExtension sectionExtension =
                                Marshal.ByteArrayToStructureLittleEndian <ElToritoSectionEntryExtension>(vdSector,
                                                                                                         toritoOff,
                                                                                                         EL_TORITO_ENTRY_SIZE);

                            toritoOff += EL_TORITO_ENTRY_SIZE;

                            if (!sectionExtension.extension_flags.HasFlag(ElToritoFlags.Continued))
                            {
                                break;
                            }
                        }
                    }

                    if (sectionHeader.header_id == ElToritoIndicator.LastHeader)
                    {
                        break;
                    }
                }
            }

exit_torito:

            if (refareas.Count > 0)
            {
                isoMetadata.Append(suspInformation);
            }

            XmlFsType.Type = fsFormat;

            if (jolietvd != null)
            {
                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;
                }
            }
            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;
                }
            }

            XmlFsType.Bootable   |= bvd != null || segaCd != null || saturn != null || dreamcast != null;
            XmlFsType.Clusters    = decodedVd.Blocks;
            XmlFsType.ClusterSize = decodedVd.BlockSize;

            information = isoMetadata.ToString();
        }
Пример #11
0
        public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, Encoding encoding)
        {
            Encoding    = encoding ?? Encoding.GetEncoding("iso-8859-15");
            information = "";

            StringBuilder sb = new StringBuilder();

            BigEndianBitConverter.IsLittleEndian =
                true; // Start in little endian until we know what are we handling here
            int  start    = 0;
            bool xenix    = false;
            bool sysv     = false;
            bool sys7th   = false;
            bool coherent = false;
            bool xenix3   = false;

            byte[] sb_sector;
            byte   sb_size_in_sectors;
            int    offset = 0;

            if (imagePlugin.Info.SectorSize <= 0x400
                ) // Check if underlying device sector size is smaller than SuperBlock size
            {
                sb_size_in_sectors = (byte)(0x400 / imagePlugin.Info.SectorSize);
            }
            else
            {
                sb_size_in_sectors = 1;  // If not a single sector can store it
            }
            // Sectors in a cylinder
            int spc = (int)(imagePlugin.Info.Heads * imagePlugin.Info.SectorsPerTrack);

            // Superblock can start on 0x000, 0x200, 0x600 and 0x800, not aligned, so we assume 16 (128 bytes/sector) sectors as a safe value
            int[] locations =
            {
                0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
                // Superblock can also skip one cylinder (for boot)
                spc
            };

            foreach (int i in locations)
            {
                sb_sector = imagePlugin.ReadSectors((ulong)i + partition.Start, sb_size_in_sectors);
                uint magic = BigEndianBitConverter.ToUInt32(sb_sector, 0x3F8);

                if (magic == XENIX_MAGIC || magic == SYSV_MAGIC)
                {
                    BigEndianBitConverter.IsLittleEndian = true; // Little endian
                    if (magic == SYSV_MAGIC)
                    {
                        sysv   = true;
                        offset = 0x200;
                    }
                    else
                    {
                        xenix = true;
                    }
                    start = i;
                    break;
                }

                if (magic == XENIX_CIGAM || magic == SYSV_CIGAM)
                {
                    BigEndianBitConverter.IsLittleEndian = false; // Big endian
                    if (magic == SYSV_CIGAM)
                    {
                        sysv   = true;
                        offset = 0x200;
                    }
                    else
                    {
                        xenix = true;
                    }
                    start = i;
                    break;
                }

                magic = BigEndianBitConverter.ToUInt32(sb_sector, 0x1F0); // XENIX 3 magic location

                if (magic == XENIX_MAGIC)
                {
                    BigEndianBitConverter.IsLittleEndian = true; // Little endian
                    xenix3 = true;
                    start  = i;
                    break;
                }

                if (magic == XENIX_CIGAM)
                {
                    BigEndianBitConverter.IsLittleEndian = false; // Big endian
                    xenix3 = true;
                    start  = i;
                    break;
                }

                magic = BigEndianBitConverter.ToUInt32(sb_sector, 0x1F8); // XENIX magic location

                if (magic == SYSV_MAGIC)
                {
                    BigEndianBitConverter.IsLittleEndian = true; // Little endian
                    sysv  = true;
                    start = i;
                    break;
                }

                if (magic == SYSV_CIGAM)
                {
                    BigEndianBitConverter.IsLittleEndian = false; // Big endian
                    sysv  = true;
                    start = i;
                    break;
                }

                byte[] coherent_string = new byte[6];
                Array.Copy(sb_sector, 0x1E4, coherent_string, 0, 6); // Coherent UNIX s_fname location
                string s_fname = StringHandlers.CToString(coherent_string, Encoding);
                Array.Copy(sb_sector, 0x1EA, coherent_string, 0, 6); // Coherent UNIX s_fpack location
                string s_fpack = StringHandlers.CToString(coherent_string, Encoding);

                if (s_fname == COH_FNAME && s_fpack == COH_FPACK || s_fname == COH_XXXXX && s_fpack == COH_XXXXX ||
                    s_fname == COH_XXXXS && s_fpack == COH_XXXXN)
                {
                    BigEndianBitConverter.IsLittleEndian = true; // Coherent is in PDP endianness, use helper for that
                    coherent = true;
                    start    = i;
                    break;
                }

                // Now try to identify 7th edition
                uint   s_fsize  = BitConverter.ToUInt32(sb_sector, 0x002);
                ushort s_nfree  = BitConverter.ToUInt16(sb_sector, 0x006);
                ushort s_ninode = BitConverter.ToUInt16(sb_sector, 0x0D0);

                if (s_fsize <= 0 || s_fsize >= 0xFFFFFFFF || s_nfree <= 0 || s_nfree >= 0xFFFF || s_ninode <= 0 ||
                    s_ninode >= 0xFFFF)
                {
                    continue;
                }

                if ((s_fsize & 0xFF) == 0x00 && (s_nfree & 0xFF) == 0x00 && (s_ninode & 0xFF) == 0x00)
                {
                    // Byteswap
                    s_fsize = ((s_fsize & 0xFF) << 24) + ((s_fsize & 0xFF00) << 8) + ((s_fsize & 0xFF0000) >> 8) +
                              ((s_fsize & 0xFF000000) >> 24);
                    s_nfree  = (ushort)(s_nfree >> 8);
                    s_ninode = (ushort)(s_ninode >> 8);
                }

                if ((s_fsize & 0xFF000000) != 0x00 || (s_nfree & 0xFF00) != 0x00 ||
                    (s_ninode & 0xFF00) != 0x00)
                {
                    continue;
                }

                if (s_fsize >= V7_MAXSIZE || s_nfree >= V7_NICFREE || s_ninode >= V7_NICINOD)
                {
                    continue;
                }

                if (s_fsize * 1024 != (partition.End - partition.Start) * imagePlugin.Info.SectorSize &&
                    s_fsize * 512 != (partition.End - partition.Start) * imagePlugin.Info.SectorSize)
                {
                    continue;
                }

                sys7th = true;
                BigEndianBitConverter.IsLittleEndian = true;
                start = i;
                break;
            }

            if (!sys7th && !sysv && !coherent && !xenix && !xenix3)
            {
                return;
            }

            XmlFsType = new FileSystemType();

            if (xenix || xenix3)
            {
                byte[]          xenix_strings = new byte[6];
                XenixSuperBlock xnx_sb        = new XenixSuperBlock();
                sb_sector = imagePlugin.ReadSectors((ulong)start + partition.Start, sb_size_in_sectors);

                if (xenix3)
                {
                    xnx_sb.s_isize   = BigEndianBitConverter.ToUInt16(sb_sector, 0x000);
                    xnx_sb.s_fsize   = BigEndianBitConverter.ToUInt32(sb_sector, 0x002);
                    xnx_sb.s_nfree   = BigEndianBitConverter.ToUInt16(sb_sector, 0x006);
                    xnx_sb.s_ninode  = BigEndianBitConverter.ToUInt16(sb_sector, 0x0D0);
                    xnx_sb.s_flock   = sb_sector[0x19A];
                    xnx_sb.s_ilock   = sb_sector[0x19B];
                    xnx_sb.s_fmod    = sb_sector[0x19C];
                    xnx_sb.s_ronly   = sb_sector[0x19D];
                    xnx_sb.s_time    = BigEndianBitConverter.ToInt32(sb_sector, 0x19E);
                    xnx_sb.s_tfree   = BigEndianBitConverter.ToUInt32(sb_sector, 0x1A2);
                    xnx_sb.s_tinode  = BigEndianBitConverter.ToUInt16(sb_sector, 0x1A6);
                    xnx_sb.s_cylblks = BigEndianBitConverter.ToUInt16(sb_sector, 0x1A8);
                    xnx_sb.s_gapblks = BigEndianBitConverter.ToUInt16(sb_sector, 0x1AA);
                    xnx_sb.s_dinfo0  = BigEndianBitConverter.ToUInt16(sb_sector, 0x1AC);
                    xnx_sb.s_dinfo1  = BigEndianBitConverter.ToUInt16(sb_sector, 0x1AE);
                    Array.Copy(sb_sector, 0x1B0, xenix_strings, 0, 6);
                    xnx_sb.s_fname = StringHandlers.CToString(xenix_strings, Encoding);
                    Array.Copy(sb_sector, 0x1B6, xenix_strings, 0, 6);
                    xnx_sb.s_fpack = StringHandlers.CToString(xenix_strings, Encoding);
                    xnx_sb.s_clean = sb_sector[0x1BC];
                    xnx_sb.s_magic = BigEndianBitConverter.ToUInt32(sb_sector, 0x1F0);
                    xnx_sb.s_type  = BigEndianBitConverter.ToUInt32(sb_sector, 0x1F4);
                }
                else
                {
                    xnx_sb.s_isize   = BigEndianBitConverter.ToUInt16(sb_sector, 0x000);
                    xnx_sb.s_fsize   = BigEndianBitConverter.ToUInt32(sb_sector, 0x002);
                    xnx_sb.s_nfree   = BigEndianBitConverter.ToUInt16(sb_sector, 0x006);
                    xnx_sb.s_ninode  = BigEndianBitConverter.ToUInt16(sb_sector, 0x198);
                    xnx_sb.s_flock   = sb_sector[0x262];
                    xnx_sb.s_ilock   = sb_sector[0x263];
                    xnx_sb.s_fmod    = sb_sector[0x264];
                    xnx_sb.s_ronly   = sb_sector[0x265];
                    xnx_sb.s_time    = BigEndianBitConverter.ToInt32(sb_sector, 0x266);
                    xnx_sb.s_tfree   = BigEndianBitConverter.ToUInt32(sb_sector, 0x26A);
                    xnx_sb.s_tinode  = BigEndianBitConverter.ToUInt16(sb_sector, 0x26E);
                    xnx_sb.s_cylblks = BigEndianBitConverter.ToUInt16(sb_sector, 0x270);
                    xnx_sb.s_gapblks = BigEndianBitConverter.ToUInt16(sb_sector, 0x272);
                    xnx_sb.s_dinfo0  = BigEndianBitConverter.ToUInt16(sb_sector, 0x274);
                    xnx_sb.s_dinfo1  = BigEndianBitConverter.ToUInt16(sb_sector, 0x276);
                    Array.Copy(sb_sector, 0x278, xenix_strings, 0, 6);
                    xnx_sb.s_fname = StringHandlers.CToString(xenix_strings, Encoding);
                    Array.Copy(sb_sector, 0x27E, xenix_strings, 0, 6);
                    xnx_sb.s_fpack = StringHandlers.CToString(xenix_strings, Encoding);
                    xnx_sb.s_clean = sb_sector[0x284];
                    xnx_sb.s_magic = BigEndianBitConverter.ToUInt32(sb_sector, 0x3F8);
                    xnx_sb.s_type  = BigEndianBitConverter.ToUInt32(sb_sector, 0x3FC);
                }

                uint bs = 512;
                sb.AppendLine("XENIX filesystem");
                XmlFsType.Type = "XENIX fs";
                switch (xnx_sb.s_type)
                {
                case 1:
                    sb.AppendLine("512 bytes per block");
                    XmlFsType.ClusterSize = 512;
                    break;

                case 2:
                    sb.AppendLine("1024 bytes per block");
                    bs = 1024;
                    XmlFsType.ClusterSize = 1024;
                    break;

                case 3:
                    sb.AppendLine("2048 bytes per block");
                    bs = 2048;
                    XmlFsType.ClusterSize = 2048;
                    break;

                default:
                    sb.AppendFormat("Unknown s_type value: 0x{0:X8}", xnx_sb.s_type).AppendLine();
                    break;
                }

                if (imagePlugin.Info.SectorSize == 2336 || imagePlugin.Info.SectorSize == 2352 ||
                    imagePlugin.Info.SectorSize == 2448)
                {
                    if (bs != 2048)
                    {
                        sb
                        .AppendFormat("WARNING: Filesystem indicates {0} bytes/block while device indicates {1} bytes/sector",
                                      bs, 2048).AppendLine();
                    }
                }
                else
                {
                    if (bs != imagePlugin.Info.SectorSize)
                    {
                        sb
                        .AppendFormat("WARNING: Filesystem indicates {0} bytes/block while device indicates {1} bytes/sector",
                                      bs, imagePlugin.Info.SectorSize).AppendLine();
                    }
                }
                sb.AppendFormat("{0} zones on volume ({1} bytes)", xnx_sb.s_fsize, xnx_sb.s_fsize * bs).AppendLine();
                sb.AppendFormat("{0} free zones on volume ({1} bytes)", xnx_sb.s_tfree, xnx_sb.s_tfree * bs)
                .AppendLine();
                sb.AppendFormat("{0} free blocks on list ({1} bytes)", xnx_sb.s_nfree, xnx_sb.s_nfree * bs)
                .AppendLine();
                sb.AppendFormat("{0} blocks per cylinder ({1} bytes)", xnx_sb.s_cylblks, xnx_sb.s_cylblks * bs)
                .AppendLine();
                sb.AppendFormat("{0} blocks per gap ({1} bytes)", xnx_sb.s_gapblks, xnx_sb.s_gapblks * bs).AppendLine();
                sb.AppendFormat("First data zone: {0}", xnx_sb.s_isize).AppendLine();
                sb.AppendFormat("{0} free inodes on volume", xnx_sb.s_tinode).AppendLine();
                sb.AppendFormat("{0} free inodes on list", xnx_sb.s_ninode).AppendLine();
                if (xnx_sb.s_flock > 0)
                {
                    sb.AppendLine("Free block list is locked");
                }
                if (xnx_sb.s_ilock > 0)
                {
                    sb.AppendLine("inode cache is locked");
                }
                if (xnx_sb.s_fmod > 0)
                {
                    sb.AppendLine("Superblock is being modified");
                }
                if (xnx_sb.s_ronly > 0)
                {
                    sb.AppendLine("Volume is mounted read-only");
                }
                sb.AppendFormat("Superblock last updated on {0}", DateHandlers.UnixToDateTime(xnx_sb.s_time))
                .AppendLine();
                if (xnx_sb.s_time != 0)
                {
                    XmlFsType.ModificationDate          = DateHandlers.UnixToDateTime(xnx_sb.s_time);
                    XmlFsType.ModificationDateSpecified = true;
                }
                sb.AppendFormat("Volume name: {0}", xnx_sb.s_fname).AppendLine();
                XmlFsType.VolumeName = xnx_sb.s_fname;
                sb.AppendFormat("Pack name: {0}", xnx_sb.s_fpack).AppendLine();
                if (xnx_sb.s_clean == 0x46)
                {
                    sb.AppendLine("Volume is clean");
                }
                else
                {
                    sb.AppendLine("Volume is dirty");
                    XmlFsType.Dirty = true;
                }
            }

            if (sysv)
            {
                sb_sector = imagePlugin.ReadSectors((ulong)start + partition.Start, sb_size_in_sectors);
                byte[] sysv_strings = new byte[6];

                SystemVRelease4SuperBlock sysv_sb = new SystemVRelease4SuperBlock
                {
                    s_type = BigEndianBitConverter.ToUInt32(sb_sector, 0x1FC + offset)
                };
                uint bs = 512;
                switch (sysv_sb.s_type)
                {
                case 1:
                    XmlFsType.ClusterSize = 512;
                    break;

                case 2:
                    bs = 1024;
                    XmlFsType.ClusterSize = 1024;
                    break;

                case 3:
                    bs = 2048;
                    XmlFsType.ClusterSize = 2048;
                    break;

                default:
                    sb.AppendFormat("Unknown s_type value: 0x{0:X8}", sysv_sb.s_type).AppendLine();
                    break;
                }

                sysv_sb.s_fsize = BigEndianBitConverter.ToUInt32(sb_sector, 0x002 + offset);

                bool sysvr4 = sysv_sb.s_fsize * bs <= 0 || sysv_sb.s_fsize * bs != partition.Size;

                if (sysvr4)
                {
                    sysv_sb.s_isize   = BigEndianBitConverter.ToUInt16(sb_sector, 0x000 + offset);
                    sysv_sb.s_state   = BigEndianBitConverter.ToUInt32(sb_sector, 0x1F4 + offset);
                    sysv_sb.s_magic   = BigEndianBitConverter.ToUInt32(sb_sector, 0x1F8 + offset);
                    sysv_sb.s_fsize   = BigEndianBitConverter.ToUInt32(sb_sector, 0x004 + offset);
                    sysv_sb.s_nfree   = BigEndianBitConverter.ToUInt16(sb_sector, 0x008 + offset);
                    sysv_sb.s_ninode  = BigEndianBitConverter.ToUInt16(sb_sector, 0x0D4 + offset);
                    sysv_sb.s_flock   = sb_sector[0x1A0 + offset];
                    sysv_sb.s_ilock   = sb_sector[0x1A1 + offset];
                    sysv_sb.s_fmod    = sb_sector[0x1A2 + offset];
                    sysv_sb.s_ronly   = sb_sector[0x1A3 + offset];
                    sysv_sb.s_time    = BigEndianBitConverter.ToUInt32(sb_sector, 0x1A4 + offset);
                    sysv_sb.s_cylblks = BigEndianBitConverter.ToUInt16(sb_sector, 0x1A8 + offset);
                    sysv_sb.s_gapblks = BigEndianBitConverter.ToUInt16(sb_sector, 0x1AA + offset);
                    sysv_sb.s_dinfo0  = BigEndianBitConverter.ToUInt16(sb_sector, 0x1AC + offset);
                    sysv_sb.s_dinfo1  = BigEndianBitConverter.ToUInt16(sb_sector, 0x1AE + offset);
                    sysv_sb.s_tfree   = BigEndianBitConverter.ToUInt32(sb_sector, 0x1B0 + offset);
                    sysv_sb.s_tinode  = BigEndianBitConverter.ToUInt16(sb_sector, 0x1B4 + offset);
                    Array.Copy(sb_sector, 0x1B6 + offset, sysv_strings, 0, 6);
                    sysv_sb.s_fname = StringHandlers.CToString(sysv_strings, Encoding);
                    Array.Copy(sb_sector, 0x1BC + offset, sysv_strings, 0, 6);
                    sysv_sb.s_fpack = StringHandlers.CToString(sysv_strings, Encoding);
                    sb.AppendLine("System V Release 4 filesystem");
                    XmlFsType.Type = "SVR4 fs";
                }
                else
                {
                    sysv_sb.s_isize   = BigEndianBitConverter.ToUInt16(sb_sector, 0x000 + offset);
                    sysv_sb.s_state   = BigEndianBitConverter.ToUInt32(sb_sector, 0x1F4 + offset);
                    sysv_sb.s_magic   = BigEndianBitConverter.ToUInt32(sb_sector, 0x1F8 + offset);
                    sysv_sb.s_fsize   = BigEndianBitConverter.ToUInt32(sb_sector, 0x002 + offset);
                    sysv_sb.s_nfree   = BigEndianBitConverter.ToUInt16(sb_sector, 0x006 + offset);
                    sysv_sb.s_ninode  = BigEndianBitConverter.ToUInt16(sb_sector, 0x0D0 + offset);
                    sysv_sb.s_flock   = sb_sector[0x19A + offset];
                    sysv_sb.s_ilock   = sb_sector[0x19B + offset];
                    sysv_sb.s_fmod    = sb_sector[0x19C + offset];
                    sysv_sb.s_ronly   = sb_sector[0x19D + offset];
                    sysv_sb.s_time    = BigEndianBitConverter.ToUInt32(sb_sector, 0x19E + offset);
                    sysv_sb.s_cylblks = BigEndianBitConverter.ToUInt16(sb_sector, 0x1A2 + offset);
                    sysv_sb.s_gapblks = BigEndianBitConverter.ToUInt16(sb_sector, 0x1A4 + offset);
                    sysv_sb.s_dinfo0  = BigEndianBitConverter.ToUInt16(sb_sector, 0x1A6 + offset);
                    sysv_sb.s_dinfo1  = BigEndianBitConverter.ToUInt16(sb_sector, 0x1A8 + offset);
                    sysv_sb.s_tfree   = BigEndianBitConverter.ToUInt32(sb_sector, 0x1AA + offset);
                    sysv_sb.s_tinode  = BigEndianBitConverter.ToUInt16(sb_sector, 0x1AE + offset);
                    Array.Copy(sb_sector, 0x1B0 + offset, sysv_strings, 0, 6);
                    sysv_sb.s_fname = StringHandlers.CToString(sysv_strings, Encoding);
                    Array.Copy(sb_sector, 0x1B6 + offset, sysv_strings, 0, 6);
                    sysv_sb.s_fpack = StringHandlers.CToString(sysv_strings, Encoding);
                    sb.AppendLine("System V Release 2 filesystem");
                    XmlFsType.Type = "SVR2 fs";
                }
                sb.AppendFormat("{0} bytes per block", bs).AppendLine();

                XmlFsType.Clusters = sysv_sb.s_fsize;
                sb.AppendFormat("{0} zones on volume ({1} bytes)", sysv_sb.s_fsize, sysv_sb.s_fsize * bs).AppendLine();
                sb.AppendFormat("{0} free zones on volume ({1} bytes)", sysv_sb.s_tfree, sysv_sb.s_tfree * bs)
                .AppendLine();
                sb.AppendFormat("{0} free blocks on list ({1} bytes)", sysv_sb.s_nfree, sysv_sb.s_nfree * bs)
                .AppendLine();
                sb.AppendFormat("{0} blocks per cylinder ({1} bytes)", sysv_sb.s_cylblks, sysv_sb.s_cylblks * bs)
                .AppendLine();
                sb.AppendFormat("{0} blocks per gap ({1} bytes)", sysv_sb.s_gapblks, sysv_sb.s_gapblks * bs)
                .AppendLine();
                sb.AppendFormat("First data zone: {0}", sysv_sb.s_isize).AppendLine();
                sb.AppendFormat("{0} free inodes on volume", sysv_sb.s_tinode).AppendLine();
                sb.AppendFormat("{0} free inodes on list", sysv_sb.s_ninode).AppendLine();
                if (sysv_sb.s_flock > 0)
                {
                    sb.AppendLine("Free block list is locked");
                }
                if (sysv_sb.s_ilock > 0)
                {
                    sb.AppendLine("inode cache is locked");
                }
                if (sysv_sb.s_fmod > 0)
                {
                    sb.AppendLine("Superblock is being modified");
                }
                if (sysv_sb.s_ronly > 0)
                {
                    sb.AppendLine("Volume is mounted read-only");
                }
                sb.AppendFormat("Superblock last updated on {0}", DateHandlers.UnixUnsignedToDateTime(sysv_sb.s_time))
                .AppendLine();
                if (sysv_sb.s_time != 0)
                {
                    XmlFsType.ModificationDate          = DateHandlers.UnixUnsignedToDateTime(sysv_sb.s_time);
                    XmlFsType.ModificationDateSpecified = true;
                }
                sb.AppendFormat("Volume name: {0}", sysv_sb.s_fname).AppendLine();
                XmlFsType.VolumeName = sysv_sb.s_fname;
                sb.AppendFormat("Pack name: {0}", sysv_sb.s_fpack).AppendLine();
                if (sysv_sb.s_state == 0x7C269D38 - sysv_sb.s_time)
                {
                    sb.AppendLine("Volume is clean");
                }
                else
                {
                    sb.AppendLine("Volume is dirty");
                    XmlFsType.Dirty = true;
                }
            }

            if (coherent)
            {
                sb_sector = imagePlugin.ReadSectors((ulong)start + partition.Start, sb_size_in_sectors);
                CoherentSuperBlock coh_sb      = new CoherentSuperBlock();
                byte[]             coh_strings = new byte[6];

                coh_sb.s_isize  = BitConverter.ToUInt16(sb_sector, 0x000);
                coh_sb.s_fsize  = Swapping.PDPFromLittleEndian(BitConverter.ToUInt32(sb_sector, 0x002));
                coh_sb.s_nfree  = BitConverter.ToUInt16(sb_sector, 0x006);
                coh_sb.s_ninode = BitConverter.ToUInt16(sb_sector, 0x108);
                coh_sb.s_flock  = sb_sector[0x1D2];
                coh_sb.s_ilock  = sb_sector[0x1D3];
                coh_sb.s_fmod   = sb_sector[0x1D4];
                coh_sb.s_ronly  = sb_sector[0x1D5];
                coh_sb.s_time   = Swapping.PDPFromLittleEndian(BitConverter.ToUInt32(sb_sector, 0x1D6));
                coh_sb.s_tfree  = Swapping.PDPFromLittleEndian(BitConverter.ToUInt32(sb_sector, 0x1DA));
                coh_sb.s_tinode = BitConverter.ToUInt16(sb_sector, 0x1DE);
                coh_sb.s_int_m  = BitConverter.ToUInt16(sb_sector, 0x1E0);
                coh_sb.s_int_n  = BitConverter.ToUInt16(sb_sector, 0x1E2);
                Array.Copy(sb_sector, 0x1E4, coh_strings, 0, 6);
                coh_sb.s_fname = StringHandlers.CToString(coh_strings, Encoding);
                Array.Copy(sb_sector, 0x1EA, coh_strings, 0, 6);
                coh_sb.s_fpack = StringHandlers.CToString(coh_strings, Encoding);

                XmlFsType.Type        = "Coherent fs";
                XmlFsType.ClusterSize = 512;
                XmlFsType.Clusters    = coh_sb.s_fsize;

                sb.AppendLine("Coherent UNIX filesystem");
                if (imagePlugin.Info.SectorSize != 512)
                {
                    sb
                    .AppendFormat("WARNING: Filesystem indicates {0} bytes/block while device indicates {1} bytes/sector",
                                  512, 2048).AppendLine();
                }
                sb.AppendFormat("{0} zones on volume ({1} bytes)", coh_sb.s_fsize, coh_sb.s_fsize * 512).AppendLine();
                sb.AppendFormat("{0} free zones on volume ({1} bytes)", coh_sb.s_tfree, coh_sb.s_tfree * 512)
                .AppendLine();
                sb.AppendFormat("{0} free blocks on list ({1} bytes)", coh_sb.s_nfree, coh_sb.s_nfree * 512)
                .AppendLine();
                sb.AppendFormat("First data zone: {0}", coh_sb.s_isize).AppendLine();
                sb.AppendFormat("{0} free inodes on volume", coh_sb.s_tinode).AppendLine();
                sb.AppendFormat("{0} free inodes on list", coh_sb.s_ninode).AppendLine();
                if (coh_sb.s_flock > 0)
                {
                    sb.AppendLine("Free block list is locked");
                }
                if (coh_sb.s_ilock > 0)
                {
                    sb.AppendLine("inode cache is locked");
                }
                if (coh_sb.s_fmod > 0)
                {
                    sb.AppendLine("Superblock is being modified");
                }
                if (coh_sb.s_ronly > 0)
                {
                    sb.AppendLine("Volume is mounted read-only");
                }
                sb.AppendFormat("Superblock last updated on {0}", DateHandlers.UnixUnsignedToDateTime(coh_sb.s_time))
                .AppendLine();
                if (coh_sb.s_time != 0)
                {
                    XmlFsType.ModificationDate          = DateHandlers.UnixUnsignedToDateTime(coh_sb.s_time);
                    XmlFsType.ModificationDateSpecified = true;
                }
                sb.AppendFormat("Volume name: {0}", coh_sb.s_fname).AppendLine();
                XmlFsType.VolumeName = coh_sb.s_fname;
                sb.AppendFormat("Pack name: {0}", coh_sb.s_fpack).AppendLine();
            }

            if (sys7th)
            {
                sb_sector = imagePlugin.ReadSectors((ulong)start + partition.Start, sb_size_in_sectors);
                UNIX7thEditionSuperBlock v7_sb = new UNIX7thEditionSuperBlock();
                byte[] sys7_strings            = new byte[6];

                v7_sb.s_isize  = BigEndianBitConverter.ToUInt16(sb_sector, 0x000);
                v7_sb.s_fsize  = BigEndianBitConverter.ToUInt32(sb_sector, 0x002);
                v7_sb.s_nfree  = BigEndianBitConverter.ToUInt16(sb_sector, 0x006);
                v7_sb.s_ninode = BigEndianBitConverter.ToUInt16(sb_sector, 0x0D0);
                v7_sb.s_flock  = sb_sector[0x19A];
                v7_sb.s_ilock  = sb_sector[0x19B];
                v7_sb.s_fmod   = sb_sector[0x19C];
                v7_sb.s_ronly  = sb_sector[0x19D];
                v7_sb.s_time   = BigEndianBitConverter.ToUInt32(sb_sector, 0x19E);
                v7_sb.s_tfree  = BigEndianBitConverter.ToUInt32(sb_sector, 0x1A2);
                v7_sb.s_tinode = BigEndianBitConverter.ToUInt16(sb_sector, 0x1A6);
                v7_sb.s_int_m  = BigEndianBitConverter.ToUInt16(sb_sector, 0x1A8);
                v7_sb.s_int_n  = BigEndianBitConverter.ToUInt16(sb_sector, 0x1AA);
                Array.Copy(sb_sector, 0x1AC, sys7_strings, 0, 6);
                v7_sb.s_fname = StringHandlers.CToString(sys7_strings, Encoding);
                Array.Copy(sb_sector, 0x1B2, sys7_strings, 0, 6);
                v7_sb.s_fpack = StringHandlers.CToString(sys7_strings, Encoding);

                XmlFsType.Type        = "UNIX 7th Edition fs";
                XmlFsType.ClusterSize = 512;
                XmlFsType.Clusters    = v7_sb.s_fsize;
                sb.AppendLine("UNIX 7th Edition filesystem");
                if (imagePlugin.Info.SectorSize != 512)
                {
                    sb
                    .AppendFormat("WARNING: Filesystem indicates {0} bytes/block while device indicates {1} bytes/sector",
                                  512, 2048).AppendLine();
                }
                sb.AppendFormat("{0} zones on volume ({1} bytes)", v7_sb.s_fsize, v7_sb.s_fsize * 512).AppendLine();
                sb.AppendFormat("{0} free zones on volume ({1} bytes)", v7_sb.s_tfree, v7_sb.s_tfree * 512)
                .AppendLine();
                sb.AppendFormat("{0} free blocks on list ({1} bytes)", v7_sb.s_nfree, v7_sb.s_nfree * 512).AppendLine();
                sb.AppendFormat("First data zone: {0}", v7_sb.s_isize).AppendLine();
                sb.AppendFormat("{0} free inodes on volume", v7_sb.s_tinode).AppendLine();
                sb.AppendFormat("{0} free inodes on list", v7_sb.s_ninode).AppendLine();
                if (v7_sb.s_flock > 0)
                {
                    sb.AppendLine("Free block list is locked");
                }
                if (v7_sb.s_ilock > 0)
                {
                    sb.AppendLine("inode cache is locked");
                }
                if (v7_sb.s_fmod > 0)
                {
                    sb.AppendLine("Superblock is being modified");
                }
                if (v7_sb.s_ronly > 0)
                {
                    sb.AppendLine("Volume is mounted read-only");
                }
                sb.AppendFormat("Superblock last updated on {0}", DateHandlers.UnixUnsignedToDateTime(v7_sb.s_time))
                .AppendLine();
                if (v7_sb.s_time != 0)
                {
                    XmlFsType.ModificationDate          = DateHandlers.UnixUnsignedToDateTime(v7_sb.s_time);
                    XmlFsType.ModificationDateSpecified = true;
                }
                sb.AppendFormat("Volume name: {0}", v7_sb.s_fname).AppendLine();
                XmlFsType.VolumeName = v7_sb.s_fname;
                sb.AppendFormat("Pack name: {0}", v7_sb.s_fpack).AppendLine();
            }

            information = sb.ToString();

            BigEndianBitConverter.IsLittleEndian = false; // Return to default (bigendian)
        }
Пример #12
0
        public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information,
                                   Encoding encoding)
        {
            Encoding    = encoding ?? Encoding.GetEncoding("iso-8859-15");
            information = "";
            var  sb          = new StringBuilder();
            uint sectors     = QNX6_SUPER_BLOCK_SIZE / imagePlugin.Info.SectorSize;
            uint bootSectors = QNX6_BOOT_BLOCKS_SIZE / imagePlugin.Info.SectorSize;

            byte[] audiSector = imagePlugin.ReadSectors(partition.Start, sectors);
            byte[] sector     = imagePlugin.ReadSectors(partition.Start + bootSectors, sectors);

            if (sector.Length < QNX6_SUPER_BLOCK_SIZE)
            {
                return;
            }

            QNX6_AudiSuperBlock audiSb = Marshal.ByteArrayToStructureLittleEndian <QNX6_AudiSuperBlock>(audiSector);

            QNX6_SuperBlock qnxSb = Marshal.ByteArrayToStructureLittleEndian <QNX6_SuperBlock>(sector);

            bool audi = audiSb.magic == QNX6_MAGIC;

            if (audi)
            {
                sb.AppendLine("QNX6 (Audi) filesystem");
                sb.AppendFormat("Checksum: 0x{0:X8}", audiSb.checksum).AppendLine();
                sb.AppendFormat("Serial: 0x{0:X16}", audiSb.checksum).AppendLine();
                sb.AppendFormat("{0} bytes per block", audiSb.blockSize).AppendLine();
                sb.AppendFormat("{0} inodes free of {1}", audiSb.freeInodes, audiSb.numInodes).AppendLine();

                sb.AppendFormat("{0} blocks ({1} bytes) free of {2} ({3} bytes)", audiSb.freeBlocks,
                                audiSb.freeBlocks * audiSb.blockSize, audiSb.numBlocks,
                                audiSb.numBlocks * audiSb.blockSize).AppendLine();

                XmlFsType = new FileSystemType
                {
                    Type           = "QNX6 (Audi) filesystem", Clusters = audiSb.numBlocks,
                    ClusterSize    = audiSb.blockSize,
                    Bootable       = true, Files = audiSb.numInodes - audiSb.freeInodes,
                    FilesSpecified = true,
                    FreeClusters   = audiSb.freeBlocks, FreeClustersSpecified = true,
                    VolumeSerial   = $"{audiSb.serial:X16}"
                };

                //xmlFSType.VolumeName = CurrentEncoding.GetString(audiSb.id);

                information = sb.ToString();

                return;
            }

            sb.AppendLine("QNX6 filesystem");
            sb.AppendFormat("Checksum: 0x{0:X8}", qnxSb.checksum).AppendLine();
            sb.AppendFormat("Serial: 0x{0:X16}", qnxSb.checksum).AppendLine();
            sb.AppendFormat("Created on {0}", DateHandlers.UnixUnsignedToDateTime(qnxSb.ctime)).AppendLine();
            sb.AppendFormat("Last mounted on {0}", DateHandlers.UnixUnsignedToDateTime(qnxSb.atime)).AppendLine();
            sb.AppendFormat("Flags: 0x{0:X8}", qnxSb.flags).AppendLine();
            sb.AppendFormat("Version1: 0x{0:X4}", qnxSb.version1).AppendLine();
            sb.AppendFormat("Version2: 0x{0:X4}", qnxSb.version2).AppendLine();

            //sb.AppendFormat("Volume ID: \"{0}\"", CurrentEncoding.GetString(qnxSb.volumeid)).AppendLine();
            sb.AppendFormat("{0} bytes per block", qnxSb.blockSize).AppendLine();
            sb.AppendFormat("{0} inodes free of {1}", qnxSb.freeInodes, qnxSb.numInodes).AppendLine();

            sb.AppendFormat("{0} blocks ({1} bytes) free of {2} ({3} bytes)", qnxSb.freeBlocks,
                            qnxSb.freeBlocks * qnxSb.blockSize, qnxSb.numBlocks, qnxSb.numBlocks * qnxSb.blockSize).
            AppendLine();

            XmlFsType = new FileSystemType
            {
                Type                      = "QNX6 filesystem",
                Clusters                  = qnxSb.numBlocks,
                ClusterSize               = qnxSb.blockSize, Bootable = true,
                Files                     = qnxSb.numInodes - qnxSb.freeInodes,
                FilesSpecified            = true, FreeClusters = qnxSb.freeBlocks,
                FreeClustersSpecified     = true,
                VolumeSerial              = $"{qnxSb.serial:X16}",
                CreationDate              = DateHandlers.UnixUnsignedToDateTime(qnxSb.ctime),
                CreationDateSpecified     = true,
                ModificationDate          = DateHandlers.UnixUnsignedToDateTime(qnxSb.atime),
                ModificationDateSpecified = true
            };

            //xmlFSType.VolumeName = CurrentEncoding.GetString(qnxSb.volumeid);

            information = sb.ToString();
        }
Пример #13
0
        /// <inheritdoc />
        public Errno Mount(IMediaImage imagePlugin, Partition partition, Encoding encoding,
                           Dictionary <string, string> options, string @namespace)
        {
            XmlFsType = new FileSystemType();

            options ??= GetDefaultOptions();

            if (options.TryGetValue("debug", out string debugString))
            {
                bool.TryParse(debugString, out _debug);
            }

            // Default namespace
            @namespace ??= "ecs";

            switch (@namespace.ToLowerInvariant())
            {
            case "dos":
                _namespace = Namespace.Dos;

                break;

            case "nt":
                _namespace = Namespace.Nt;

                break;

            case "os2":
                _namespace = Namespace.Os2;

                break;

            case "ecs":
                _namespace = Namespace.Ecs;

                break;

            case "lfn":
                _namespace = Namespace.Lfn;

                break;

            case "human":
                _namespace = Namespace.Human;

                break;

            default: return(Errno.InvalidArgument);
            }

            AaruConsole.DebugWriteLine("FAT plugin", "Reading BPB");

            uint sectorsPerBpb = imagePlugin.Info.SectorSize < 512 ? 512 / imagePlugin.Info.SectorSize : 1;

            byte[] bpbSector = imagePlugin.ReadSectors(0 + partition.Start, sectorsPerBpb);

            BpbKind bpbKind = DetectBpbKind(bpbSector, imagePlugin, partition, out BiosParameterBlockEbpb fakeBpb,
                                            out HumanParameterBlock humanBpb, out AtariParameterBlock atariBpb,
                                            out byte minBootNearJump, out bool andosOemCorrect, out bool bootable);

            _fat12             = false;
            _fat16             = false;
            _fat32             = false;
            _useFirstFat       = true;
            XmlFsType.Bootable = bootable;

            _statfs = new FileSystemInfo
            {
                Blocks         = XmlFsType.Clusters,
                FilenameLength = 11,
                Files          = 0, // Requires traversing all directories
                FreeFiles      = 0,
                PluginId       = Id,
                FreeBlocks     = 0 // Requires traversing the FAT
            };

            // This is needed because for FAT16, GEMDOS increases bytes per sector count instead of using big_sectors field.
            uint sectorsPerRealSector = 1;

            // This is needed because some OSes don't put volume label as first entry in the root directory
            uint sectorsForRootDirectory = 0;
            uint rootDirectoryCluster    = 0;

            switch (bpbKind)
            {
            case BpbKind.DecRainbow:
            case BpbKind.Hardcoded:
            case BpbKind.Msx:
            case BpbKind.Apricot:
                _fat12 = true;

                break;

            case BpbKind.ShortFat32:
            case BpbKind.LongFat32:
            {
                _fat32 = true;

                Fat32ParameterBlock fat32Bpb =
                    Marshal.ByteArrayToStructureLittleEndian <Fat32ParameterBlock>(bpbSector);

                Fat32ParameterBlockShort shortFat32Bpb =
                    Marshal.ByteArrayToStructureLittleEndian <Fat32ParameterBlockShort>(bpbSector);

                rootDirectoryCluster = fat32Bpb.root_cluster;

                // This is to support FAT partitions on hybrid ISO/USB images
                if (imagePlugin.Info.XmlMediaType == XmlMediaType.OpticalDisc)
                {
                    fat32Bpb.bps       *= 4;
                    fat32Bpb.spc       /= 4;
                    fat32Bpb.big_spfat /= 4;
                    fat32Bpb.hsectors  /= 4;
                    fat32Bpb.sptrk     /= 4;
                }

                XmlFsType.Type = fat32Bpb.version != 0 ? "FAT+" : "FAT32";

                if (fat32Bpb.oem_name != null &&
                    (fat32Bpb.oem_name[5] != 0x49 || fat32Bpb.oem_name[6] != 0x48 || fat32Bpb.oem_name[7] != 0x43))
                {
                    XmlFsType.SystemIdentifier = StringHandlers.CToString(fat32Bpb.oem_name);
                }

                _sectorsPerCluster    = fat32Bpb.spc;
                XmlFsType.ClusterSize = (uint)(fat32Bpb.bps * fat32Bpb.spc);
                _reservedSectors      = fat32Bpb.rsectors;

                if (fat32Bpb.big_sectors == 0 &&
                    fat32Bpb.signature == 0x28)
                {
                    XmlFsType.Clusters = shortFat32Bpb.huge_sectors / shortFat32Bpb.spc;
                }
                else
                {
                    XmlFsType.Clusters = fat32Bpb.big_sectors / fat32Bpb.spc;
                }

                _sectorsPerFat         = fat32Bpb.big_spfat;
                XmlFsType.VolumeSerial = $"{fat32Bpb.serial_no:X8}";

                _statfs.Id = new FileSystemId
                {
                    IsInt    = true,
                    Serial32 = fat32Bpb.serial_no
                };

                if ((fat32Bpb.flags & 0xF8) == 0x00)
                {
                    if ((fat32Bpb.flags & 0x01) == 0x01)
                    {
                        XmlFsType.Dirty = true;
                    }
                }

                if ((fat32Bpb.mirror_flags & 0x80) == 0x80)
                {
                    _useFirstFat = (fat32Bpb.mirror_flags & 0xF) != 1;
                }

                if (fat32Bpb.signature == 0x29)
                {
                    XmlFsType.VolumeName = StringHandlers.SpacePaddedToString(fat32Bpb.volume_label, Encoding);
                    XmlFsType.VolumeName = XmlFsType.VolumeName?.Replace("\0", "");
                }

                // Check that jumps to a correct boot code position and has boot signature set.
                // This will mean that the volume will boot, even if just to say "this is not bootable change disk"......
                XmlFsType.Bootable =
                    (fat32Bpb.jump[0] == 0xEB && fat32Bpb.jump[1] >= minBootNearJump && fat32Bpb.jump[1] < 0x80) ||
                    (fat32Bpb.jump[0] == 0xE9 && fat32Bpb.jump.Length >= 3 &&
                     BitConverter.ToUInt16(fat32Bpb.jump, 1) >= minBootNearJump &&
                     BitConverter.ToUInt16(fat32Bpb.jump, 1) <= 0x1FC);

                sectorsPerRealSector = fat32Bpb.bps / imagePlugin.Info.SectorSize;
                _sectorsPerCluster  *= sectorsPerRealSector;

                // First root directory sector
                _firstClusterSector =
                    ((ulong)((fat32Bpb.big_spfat * fat32Bpb.fats_no) + fat32Bpb.rsectors) * sectorsPerRealSector) -
                    (2 * _sectorsPerCluster);

                if (fat32Bpb.fsinfo_sector + partition.Start <= partition.End)
                {
                    byte[] fsinfoSector = imagePlugin.ReadSector(fat32Bpb.fsinfo_sector + partition.Start);

                    FsInfoSector fsInfo = Marshal.ByteArrayToStructureLittleEndian <FsInfoSector>(fsinfoSector);

                    if (fsInfo.signature1 == FSINFO_SIGNATURE1 &&
                        fsInfo.signature2 == FSINFO_SIGNATURE2 &&
                        fsInfo.signature3 == FSINFO_SIGNATURE3)
                    {
                        if (fsInfo.free_clusters < 0xFFFFFFFF)
                        {
                            XmlFsType.FreeClusters          = fsInfo.free_clusters;
                            XmlFsType.FreeClustersSpecified = true;
                        }
                    }
                }

                break;
            }

            // Some fields could overflow fake BPB, those will be handled below
            case BpbKind.Atari:
            {
                ushort sum = 0;

                for (int i = 0; i < bpbSector.Length; i += 2)
                {
                    sum += BigEndianBitConverter.ToUInt16(bpbSector, i);
                }

                // TODO: Check this
                if (sum == 0x1234)
                {
                    XmlFsType.Bootable = true;
                }

                break;
            }

            case BpbKind.Human:
                // If not debug set Human68k namespace and ShiftJIS codepage as defaults
                if (!_debug)
                {
                    _namespace = Namespace.Human;
                    encoding   = Encoding.GetEncoding("shift_jis");
                }

                XmlFsType.Bootable = true;

                break;
            }

            Encoding = encoding ?? (bpbKind == BpbKind.Human ? Encoding.GetEncoding("shift_jis")
                                        : Encoding.GetEncoding("IBM437"));

            ulong firstRootSector = 0;

            if (!_fat32)
            {
                // This is to support FAT partitions on hybrid ISO/USB images
                if (imagePlugin.Info.XmlMediaType == XmlMediaType.OpticalDisc)
                {
                    fakeBpb.bps      *= 4;
                    fakeBpb.spc      /= 4;
                    fakeBpb.spfat    /= 4;
                    fakeBpb.hsectors /= 4;
                    fakeBpb.sptrk    /= 4;
                    fakeBpb.rsectors /= 4;

                    if (fakeBpb.spc == 0)
                    {
                        fakeBpb.spc = 1;
                    }
                }

                // This assumes no sane implementation will violate cluster size rules
                // However nothing prevents this to happen
                // If first file on disk uses only one cluster there is absolutely no way to differentiate between FAT12 and FAT16,
                // so let's hope implementations use common sense?
                if (!_fat12 &&
                    !_fat16)
                {
                    ulong clusters;

                    if (fakeBpb.sectors == 0)
                    {
                        clusters = fakeBpb.spc == 0 ? fakeBpb.big_sectors : fakeBpb.big_sectors / fakeBpb.spc;
                    }
                    else
                    {
                        clusters = fakeBpb.spc == 0 ? fakeBpb.sectors : (ulong)fakeBpb.sectors / fakeBpb.spc;
                    }

                    if (clusters < 4089)
                    {
                        _fat12 = true;
                    }
                    else
                    {
                        _fat16 = true;
                    }
                }

                if (_fat12)
                {
                    XmlFsType.Type = "FAT12";
                }
                else if (_fat16)
                {
                    XmlFsType.Type = "FAT16";
                }

                if (bpbKind == BpbKind.Atari)
                {
                    if (atariBpb.serial_no[0] != 0x49 ||
                        atariBpb.serial_no[1] != 0x48 ||
                        atariBpb.serial_no[2] != 0x43)
                    {
                        XmlFsType.VolumeSerial =
                            $"{atariBpb.serial_no[0]:X2}{atariBpb.serial_no[1]:X2}{atariBpb.serial_no[2]:X2}";

                        _statfs.Id = new FileSystemId
                        {
                            IsInt    = true,
                            Serial32 = (uint)((atariBpb.serial_no[0] << 16) + (atariBpb.serial_no[1] << 8) +
                                              atariBpb.serial_no[2])
                        };
                    }

                    XmlFsType.SystemIdentifier = StringHandlers.CToString(atariBpb.oem_name);

                    if (string.IsNullOrEmpty(XmlFsType.SystemIdentifier))
                    {
                        XmlFsType.SystemIdentifier = null;
                    }
                }
                else if (fakeBpb.oem_name != null)
                {
                    if (fakeBpb.oem_name[5] != 0x49 ||
                        fakeBpb.oem_name[6] != 0x48 ||
                        fakeBpb.oem_name[7] != 0x43)
                    {
                        // Later versions of Windows create a DOS 3 BPB without OEM name on 8 sectors/track floppies
                        // OEM ID should be ASCII, otherwise ignore it
                        if (fakeBpb.oem_name[0] >= 0x20 &&
                            fakeBpb.oem_name[0] <= 0x7F &&
                            fakeBpb.oem_name[1] >= 0x20 &&
                            fakeBpb.oem_name[1] <= 0x7F &&
                            fakeBpb.oem_name[2] >= 0x20 &&
                            fakeBpb.oem_name[2] <= 0x7F &&
                            fakeBpb.oem_name[3] >= 0x20 &&
                            fakeBpb.oem_name[3] <= 0x7F &&
                            fakeBpb.oem_name[4] >= 0x20 &&
                            fakeBpb.oem_name[4] <= 0x7F &&
                            fakeBpb.oem_name[5] >= 0x20 &&
                            fakeBpb.oem_name[5] <= 0x7F &&
                            fakeBpb.oem_name[6] >= 0x20 &&
                            fakeBpb.oem_name[6] <= 0x7F &&
                            fakeBpb.oem_name[7] >= 0x20 &&
                            fakeBpb.oem_name[7] <= 0x7F)
                        {
                            XmlFsType.SystemIdentifier = StringHandlers.CToString(fakeBpb.oem_name);
                        }
                        else if (fakeBpb.oem_name[0] < 0x20 &&
                                 fakeBpb.oem_name[1] >= 0x20 &&
                                 fakeBpb.oem_name[1] <= 0x7F &&
                                 fakeBpb.oem_name[2] >= 0x20 &&
                                 fakeBpb.oem_name[2] <= 0x7F &&
                                 fakeBpb.oem_name[3] >= 0x20 &&
                                 fakeBpb.oem_name[3] <= 0x7F &&
                                 fakeBpb.oem_name[4] >= 0x20 &&
                                 fakeBpb.oem_name[4] <= 0x7F &&
                                 fakeBpb.oem_name[5] >= 0x20 &&
                                 fakeBpb.oem_name[5] <= 0x7F &&
                                 fakeBpb.oem_name[6] >= 0x20 &&
                                 fakeBpb.oem_name[6] <= 0x7F &&
                                 fakeBpb.oem_name[7] >= 0x20 &&
                                 fakeBpb.oem_name[7] <= 0x7F)
                        {
                            XmlFsType.SystemIdentifier = StringHandlers.CToString(fakeBpb.oem_name, Encoding, start: 1);
                        }
                    }

                    if (fakeBpb.signature == 0x28 ||
                        fakeBpb.signature == 0x29)
                    {
                        XmlFsType.VolumeSerial = $"{fakeBpb.serial_no:X8}";

                        _statfs.Id = new FileSystemId
                        {
                            IsInt    = true,
                            Serial32 = fakeBpb.serial_no
                        };
                    }
                }

                if (bpbKind != BpbKind.Human)
                {
                    if (fakeBpb.sectors == 0)
                    {
                        XmlFsType.Clusters = fakeBpb.spc == 0 ? fakeBpb.big_sectors : fakeBpb.big_sectors / fakeBpb.spc;
                    }
                    else
                    {
                        XmlFsType.Clusters =
                            (ulong)(fakeBpb.spc == 0 ? fakeBpb.sectors : fakeBpb.sectors / fakeBpb.spc);
                    }
                }
                else
                {
                    XmlFsType.Clusters = humanBpb.clusters == 0 ? humanBpb.big_clusters : humanBpb.clusters;
                }

                _sectorsPerCluster    = fakeBpb.spc;
                XmlFsType.ClusterSize = (uint)(fakeBpb.bps * fakeBpb.spc);
                _reservedSectors      = fakeBpb.rsectors;
                _sectorsPerFat        = fakeBpb.spfat;

                if (fakeBpb.signature == 0x28 ||
                    fakeBpb.signature == 0x29 ||
                    andosOemCorrect)
                {
                    if ((fakeBpb.flags & 0xF8) == 0x00)
                    {
                        if ((fakeBpb.flags & 0x01) == 0x01)
                        {
                            XmlFsType.Dirty = true;
                        }
                    }

                    if (fakeBpb.signature == 0x29 || andosOemCorrect)
                    {
                        XmlFsType.VolumeName = StringHandlers.SpacePaddedToString(fakeBpb.volume_label, Encoding);
                        XmlFsType.VolumeName = XmlFsType.VolumeName?.Replace("\0", "");
                    }
                }

                // Workaround that PCExchange jumps into "FAT16   "...
                if (XmlFsType.SystemIdentifier == "PCX 2.0 ")
                {
                    fakeBpb.jump[1] += 8;
                }

                // Check that jumps to a correct boot code position and has boot signature set.
                // This will mean that the volume will boot, even if just to say "this is not bootable change disk"......
                if (XmlFsType.Bootable == false &&
                    fakeBpb.jump != null)
                {
                    XmlFsType.Bootable |=
                        (fakeBpb.jump[0] == 0xEB && fakeBpb.jump[1] >= minBootNearJump && fakeBpb.jump[1] < 0x80) ||
                        (fakeBpb.jump[0] == 0xE9 && fakeBpb.jump.Length >= 3 &&
                         BitConverter.ToUInt16(fakeBpb.jump, 1) >= minBootNearJump &&
                         BitConverter.ToUInt16(fakeBpb.jump, 1) <= 0x1FC);
                }

                // First root directory sector
                firstRootSector =
                    ((ulong)((fakeBpb.spfat * fakeBpb.fats_no) + fakeBpb.rsectors) * sectorsPerRealSector) +
                    partition.Start;

                sectorsForRootDirectory = (uint)((fakeBpb.root_ent * 32) / imagePlugin.Info.SectorSize);

                sectorsPerRealSector = fakeBpb.bps / imagePlugin.Info.SectorSize;
                _sectorsPerCluster  *= sectorsPerRealSector;
            }

            _firstClusterSector += partition.Start;

            _image = imagePlugin;

            if (_fat32)
            {
                _fatEntriesPerSector = imagePlugin.Info.SectorSize / 4;
            }
            else if (_fat16)
            {
                _fatEntriesPerSector = imagePlugin.Info.SectorSize / 2;
            }
            else
            {
                _fatEntriesPerSector = (imagePlugin.Info.SectorSize * 2) / 3;
            }

            _fatFirstSector = partition.Start + (_reservedSectors * sectorsPerRealSector);

            _rootDirectoryCache = new Dictionary <string, CompleteDirectoryEntry>();
            byte[] rootDirectory;

            if (!_fat32)
            {
                _firstClusterSector = (firstRootSector + sectorsForRootDirectory) - (_sectorsPerCluster * 2);
                rootDirectory       = imagePlugin.ReadSectors(firstRootSector, sectorsForRootDirectory);

                if (bpbKind == BpbKind.DecRainbow)
                {
                    var rootMs = new MemoryStream();

                    foreach (byte[] tmp in from ulong rootSector in new[]
                    {
                        0x17, 0x19, 0x1B, 0x1D, 0x1E, 0x20
                    } select imagePlugin.ReadSector(rootSector))
                    {
                        rootMs.Write(tmp, 0, tmp.Length);
                    }

                    rootDirectory = rootMs.ToArray();
                }
            }
            else
            {
                if (rootDirectoryCluster == 0)
                {
                    return(Errno.InvalidArgument);
                }

                var    rootMs = new MemoryStream();
                uint[] rootDirectoryClusters = GetClusters(rootDirectoryCluster);

                foreach (byte[] buffer in rootDirectoryClusters.Select(cluster =>
                                                                       imagePlugin.
                                                                       ReadSectors(_firstClusterSector + (cluster * _sectorsPerCluster),
                                                                                   _sectorsPerCluster)))
                {
                    rootMs.Write(buffer, 0, buffer.Length);
                }

                rootDirectory = rootMs.ToArray();

                // OS/2 FAT32.IFS uses LFN instead of .LONGNAME
                if (_namespace == Namespace.Os2)
                {
                    _namespace = Namespace.Os2;
                }
            }

            if (rootDirectory is null)
            {
                return(Errno.InvalidArgument);
            }

            byte[] lastLfnName     = null;
            byte   lastLfnChecksum = 0;

            for (int i = 0; i < rootDirectory.Length; i += Marshal.SizeOf <DirectoryEntry>())
            {
                DirectoryEntry entry =
                    Marshal.ByteArrayToStructureLittleEndian <DirectoryEntry>(rootDirectory, i,
                                                                              Marshal.SizeOf <DirectoryEntry>());

                if (entry.filename[0] == DIRENT_FINISHED)
                {
                    break;
                }

                if (entry.attributes.HasFlag(FatAttributes.LFN))
                {
                    if (_namespace != Namespace.Lfn &&
                        _namespace != Namespace.Ecs)
                    {
                        continue;
                    }

                    LfnEntry lfnEntry =
                        Marshal.ByteArrayToStructureLittleEndian <LfnEntry>(rootDirectory, i,
                                                                            Marshal.SizeOf <LfnEntry>());

                    int lfnSequence = lfnEntry.sequence & LFN_MASK;

                    if ((lfnEntry.sequence & LFN_ERASED) > 0)
                    {
                        continue;
                    }

                    if ((lfnEntry.sequence & LFN_LAST) > 0)
                    {
                        lastLfnName     = new byte[lfnSequence * 26];
                        lastLfnChecksum = lfnEntry.checksum;
                    }

                    if (lastLfnName is null)
                    {
                        continue;
                    }

                    if (lfnEntry.checksum != lastLfnChecksum)
                    {
                        continue;
                    }

                    lfnSequence--;

                    Array.Copy(lfnEntry.name1, 0, lastLfnName, lfnSequence * 26, 10);
                    Array.Copy(lfnEntry.name2, 0, lastLfnName, (lfnSequence * 26) + 10, 12);
                    Array.Copy(lfnEntry.name3, 0, lastLfnName, (lfnSequence * 26) + 22, 4);

                    continue;
                }

                // Not a correct entry
                if (entry.filename[0] < DIRENT_MIN &&
                    entry.filename[0] != DIRENT_E5)
                {
                    continue;
                }

                // Self
                if (Encoding.GetString(entry.filename).TrimEnd() == ".")
                {
                    continue;
                }

                // Parent
                if (Encoding.GetString(entry.filename).TrimEnd() == "..")
                {
                    continue;
                }

                // Deleted
                if (entry.filename[0] == DIRENT_DELETED)
                {
                    continue;
                }

                string filename;

                if (entry.attributes.HasFlag(FatAttributes.VolumeLabel))
                {
                    byte[] fullname = new byte[11];
                    Array.Copy(entry.filename, 0, fullname, 0, 8);
                    Array.Copy(entry.extension, 0, fullname, 8, 3);
                    string volname = Encoding.GetString(fullname).Trim();

                    if (!string.IsNullOrEmpty(volname))
                    {
                        XmlFsType.VolumeName =
                            entry.caseinfo.HasFlag(CaseInfo.AllLowerCase) && _namespace == Namespace.Nt
                                ? volname.ToLower() : volname;
                    }

                    XmlFsType.VolumeName = XmlFsType.VolumeName?.Replace("\0", "");

                    if (entry.ctime > 0 &&
                        entry.cdate > 0)
                    {
                        XmlFsType.CreationDate = DateHandlers.DosToDateTime(entry.cdate, entry.ctime);

                        if (entry.ctime_ms > 0)
                        {
                            XmlFsType.CreationDate = XmlFsType.CreationDate.AddMilliseconds(entry.ctime_ms * 10);
                        }

                        XmlFsType.CreationDateSpecified = true;
                    }

                    if (entry.mtime > 0 &&
                        entry.mdate > 0)
                    {
                        XmlFsType.ModificationDate          = DateHandlers.DosToDateTime(entry.mdate, entry.mtime);
                        XmlFsType.ModificationDateSpecified = true;
                    }

                    continue;
                }

                var completeEntry = new CompleteDirectoryEntry
                {
                    Dirent = entry
                };

                if ((_namespace == Namespace.Lfn || _namespace == Namespace.Ecs) &&
                    lastLfnName != null)
                {
                    byte calculatedLfnChecksum = LfnChecksum(entry.filename, entry.extension);

                    if (calculatedLfnChecksum == lastLfnChecksum)
                    {
                        filename = StringHandlers.CToString(lastLfnName, Encoding.Unicode, true);

                        completeEntry.Lfn = filename;
                        lastLfnName       = null;
                        lastLfnChecksum   = 0;
                    }
                }

                if (entry.filename[0] == DIRENT_E5)
                {
                    entry.filename[0] = DIRENT_DELETED;
                }

                string name      = Encoding.GetString(entry.filename).TrimEnd();
                string extension = Encoding.GetString(entry.extension).TrimEnd();

                if (_namespace == Namespace.Nt)
                {
                    if (entry.caseinfo.HasFlag(CaseInfo.LowerCaseExtension))
                    {
                        extension = extension.ToLower(CultureInfo.CurrentCulture);
                    }

                    if (entry.caseinfo.HasFlag(CaseInfo.LowerCaseBasename))
                    {
                        name = name.ToLower(CultureInfo.CurrentCulture);
                    }
                }

                if (extension != "")
                {
                    filename = name + "." + extension;
                }
                else
                {
                    filename = name;
                }

                completeEntry.Shortname = filename;

                if (_namespace == Namespace.Human)
                {
                    HumanDirectoryEntry humanEntry =
                        Marshal.ByteArrayToStructureLittleEndian <HumanDirectoryEntry>(rootDirectory, i,
                                                                                       Marshal.SizeOf <HumanDirectoryEntry>());

                    completeEntry.HumanDirent = humanEntry;

                    name      = StringHandlers.CToString(humanEntry.name1, Encoding).TrimEnd();
                    extension = StringHandlers.CToString(humanEntry.extension, Encoding).TrimEnd();
                    string name2 = StringHandlers.CToString(humanEntry.name2, Encoding).TrimEnd();

                    if (extension != "")
                    {
                        filename = name + name2 + "." + extension;
                    }
                    else
                    {
                        filename = name + name2;
                    }

                    completeEntry.HumanName = filename;
                }

                if (!_fat32 &&
                    filename == "EA DATA. SF")
                {
                    _eaDirEntry     = entry;
                    lastLfnName     = null;
                    lastLfnChecksum = 0;

                    if (_debug)
                    {
                        _rootDirectoryCache[completeEntry.ToString()] = completeEntry;
                    }

                    continue;
                }

                _rootDirectoryCache[completeEntry.ToString()] = completeEntry;
                lastLfnName     = null;
                lastLfnChecksum = 0;
            }

            XmlFsType.VolumeName = XmlFsType.VolumeName?.Trim();
            _statfs.Blocks       = XmlFsType.Clusters;

            switch (bpbKind)
            {
            case BpbKind.Hardcoded:
                _statfs.Type = $"Microsoft FAT{(_fat16 ? "16" : "12")}";

                break;

            case BpbKind.Atari:
                _statfs.Type = $"Atari FAT{(_fat16 ? "16" : "12")}";

                break;

            case BpbKind.Msx:
                _statfs.Type = $"MSX FAT{(_fat16 ? "16" : "12")}";

                break;

            case BpbKind.Dos2:
            case BpbKind.Dos3:
            case BpbKind.Dos32:
            case BpbKind.Dos33:
            case BpbKind.ShortExtended:
            case BpbKind.Extended:
                _statfs.Type = $"Microsoft FAT{(_fat16 ? "16" : "12")}";

                break;

            case BpbKind.ShortFat32:
            case BpbKind.LongFat32:
                _statfs.Type = XmlFsType.Type == "FAT+" ? "FAT+" : "Microsoft FAT32";

                break;

            case BpbKind.Andos:
                _statfs.Type = $"ANDOS FAT{(_fat16 ? "16" : "12")}";

                break;

            case BpbKind.Apricot:
                _statfs.Type = $"Apricot FAT{(_fat16 ? "16" : "12")}";

                break;

            case BpbKind.DecRainbow:
                _statfs.Type = $"DEC FAT{(_fat16 ? "16" : "12")}";

                break;

            case BpbKind.Human:
                _statfs.Type = $"Human68k FAT{(_fat16 ? "16" : "12")}";

                break;

            default: throw new ArgumentOutOfRangeException();
            }

            _bytesPerCluster = _sectorsPerCluster * imagePlugin.Info.SectorSize;

            if (_fat12)
            {
                byte[] fatBytes =
                    imagePlugin.ReadSectors(_fatFirstSector + (_useFirstFat ? 0 : _sectorsPerFat), _sectorsPerFat);

                _fatEntries = new ushort[_statfs.Blocks];

                int pos = 0;

                for (int i = 0; i + 3 < fatBytes.Length && pos < _fatEntries.Length; i += 3)
                {
                    _fatEntries[pos++] = (ushort)(((fatBytes[i + 1] & 0xF) << 8) + fatBytes[i + 0]);
                    _fatEntries[pos++] = (ushort)(((fatBytes[i + 1] & 0xF0) >> 4) + (fatBytes[i + 2] << 4));
                }
            }
            else if (_fat16)
            {
                AaruConsole.DebugWriteLine("FAT plugin", "Reading FAT16");

                byte[] fatBytes =
                    imagePlugin.ReadSectors(_fatFirstSector + (_useFirstFat ? 0 : _sectorsPerFat), _sectorsPerFat);

                AaruConsole.DebugWriteLine("FAT plugin", "Casting FAT");
                _fatEntries = MemoryMarshal.Cast <byte, ushort>(fatBytes).ToArray();
            }

            // TODO: Check how this affects international filenames
            _cultureInfo    = new CultureInfo("en-US", false);
            _directoryCache = new Dictionary <string, Dictionary <string, CompleteDirectoryEntry> >();

            // Check it is really an OS/2 EA file
            if (_eaDirEntry.start_cluster != 0)
            {
                CacheEaData();
                ushort eamagic = BitConverter.ToUInt16(_cachedEaData, 0);

                if (eamagic != EADATA_MAGIC)
                {
                    _eaDirEntry   = new DirectoryEntry();
                    _cachedEaData = null;
                }
                else
                {
                    _eaCache = new Dictionary <string, Dictionary <string, byte[]> >();
                }
            }
            else if (_fat32)
            {
                _eaCache = new Dictionary <string, Dictionary <string, byte[]> >();
            }

            // Check OS/2 .LONGNAME
            if (_eaCache != null &&
                (_namespace == Namespace.Os2 || _namespace == Namespace.Ecs) &&
                !_fat32)
            {
                List <KeyValuePair <string, CompleteDirectoryEntry> > rootFilesWithEas =
                    _rootDirectoryCache.Where(t => t.Value.Dirent.ea_handle != 0).ToList();

                foreach (KeyValuePair <string, CompleteDirectoryEntry> fileWithEa in rootFilesWithEas)
                {
                    Dictionary <string, byte[]> eas = GetEas(fileWithEa.Value.Dirent.ea_handle);

                    if (eas is null)
                    {
                        continue;
                    }

                    if (!eas.TryGetValue("com.microsoft.os2.longname", out byte[] longnameEa))
Пример #14
0
        public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information,
                                   Encoding encoding)
        {
            Encoding    = encoding ?? Encoding.GetEncoding("iso-8859-15");
            information = "";

            StringBuilder sb = new StringBuilder();

            ulong sector = AFS_BOOTBLOCK_SIZE / imagePlugin.Info.SectorSize;
            uint  offset = AFS_BOOTBLOCK_SIZE % imagePlugin.Info.SectorSize;
            uint  run    = 1;

            if (imagePlugin.Info.SectorSize < AFS_SUPERBLOCK_SIZE)
            {
                run = AFS_SUPERBLOCK_SIZE / imagePlugin.Info.SectorSize;
            }

            byte[] tmp      = imagePlugin.ReadSectors(sector + partition.Start, run);
            byte[] sbSector = new byte[AFS_SUPERBLOCK_SIZE];
            Array.Copy(tmp, offset, sbSector, 0, AFS_SUPERBLOCK_SIZE);

            AtheosSuperBlock afsSb = Marshal.ByteArrayToStructureLittleEndian <AtheosSuperBlock>(sbSector);

            sb.AppendLine("Atheos filesystem");

            if (afsSb.flags == 1)
            {
                sb.AppendLine("Filesystem is read-only");
            }

            sb.AppendFormat("Volume name: {0}", StringHandlers.CToString(afsSb.name, Encoding)).AppendLine();
            sb.AppendFormat("{0} bytes per block", afsSb.block_size).AppendLine();
            sb.AppendFormat("{0} blocks in volume ({1} bytes)", afsSb.num_blocks, afsSb.num_blocks * afsSb.block_size)
            .AppendLine();
            sb.AppendFormat("{0} used blocks ({1} bytes)", afsSb.used_blocks, afsSb.used_blocks * afsSb.block_size)
            .AppendLine();
            sb.AppendFormat("{0} bytes per i-node", afsSb.inode_size).AppendLine();
            sb.AppendFormat("{0} blocks per allocation group ({1} bytes)", afsSb.blocks_per_ag,
                            afsSb.blocks_per_ag * afsSb.block_size).AppendLine();
            sb.AppendFormat("{0} allocation groups in volume", afsSb.num_ags).AppendLine();
            sb.AppendFormat("Journal resides in block {0} of allocation group {1} and runs for {2} blocks ({3} bytes)",
                            afsSb.log_blocks_start, afsSb.log_blocks_ag, afsSb.log_blocks_len,
                            afsSb.log_blocks_len * afsSb.block_size).AppendLine();
            sb.AppendFormat("Journal starts in byte {0} and has {1} bytes in {2} blocks", afsSb.log_start,
                            afsSb.log_size, afsSb.log_valid_blocks).AppendLine();
            sb
            .AppendFormat("Root folder's i-node resides in block {0} of allocation group {1} and runs for {2} blocks ({3} bytes)",
                          afsSb.root_dir_start, afsSb.root_dir_ag, afsSb.root_dir_len,
                          afsSb.root_dir_len * afsSb.block_size).AppendLine();
            sb
            .AppendFormat("Directory containing files scheduled for deletion's i-node resides in block {0} of allocation group {1} and runs for {2} blocks ({3} bytes)",
                          afsSb.deleted_start, afsSb.deleted_ag, afsSb.deleted_len,
                          afsSb.deleted_len * afsSb.block_size).AppendLine();
            sb
            .AppendFormat("Indices' i-node resides in block {0} of allocation group {1} and runs for {2} blocks ({3} bytes)",
                          afsSb.indices_start, afsSb.indices_ag, afsSb.indices_len,
                          afsSb.indices_len * afsSb.block_size).AppendLine();
            sb.AppendFormat("{0} blocks for bootloader ({1} bytes)", afsSb.boot_size,
                            afsSb.boot_size * afsSb.block_size).AppendLine();

            information = sb.ToString();

            XmlFsType = new FileSystemType
            {
                Clusters              = (ulong)afsSb.num_blocks,
                ClusterSize           = afsSb.block_size,
                Dirty                 = false,
                FreeClusters          = (ulong)(afsSb.num_blocks - afsSb.used_blocks),
                FreeClustersSpecified = true,
                Type       = "AtheOS filesystem",
                VolumeName = StringHandlers.CToString(afsSb.name, Encoding)
            };
        }
Пример #15
0
        public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information,
                                   Encoding encoding)
        {
            Encoding    = encoding ?? Encoding.GetEncoding("iso-8859-15");
            information = "";

            var sb = new StringBuilder();

            bool newExt2 = false;
            bool ext3    = false;
            bool ext4    = false;

            int  sbSizeInBytes   = Marshal.SizeOf <SuperBlock>();
            uint sbSizeInSectors = (uint)(sbSizeInBytes / imagePlugin.Info.SectorSize);

            if (sbSizeInBytes % imagePlugin.Info.SectorSize > 0)
            {
                sbSizeInSectors++;
            }

            ulong sbSectorOff = SB_POS / imagePlugin.Info.SectorSize;
            uint  sbOff       = SB_POS % imagePlugin.Info.SectorSize;

            byte[] sbSector = imagePlugin.ReadSectors(sbSectorOff + partition.Start, sbSizeInSectors);
            byte[] sblock   = new byte[sbSizeInBytes];
            Array.Copy(sbSector, sbOff, sblock, 0, sbSizeInBytes);
            SuperBlock supblk = Marshal.ByteArrayToStructureLittleEndian <SuperBlock>(sblock);

            XmlFsType = new FileSystemType();

            switch (supblk.magic)
            {
            case EXT2_MAGIC_OLD:
                sb.AppendLine("ext2 (old) filesystem");
                XmlFsType.Type = "ext2";

                break;

            case EXT2_MAGIC:
                ext3 |= (supblk.ftr_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL) == EXT3_FEATURE_COMPAT_HAS_JOURNAL ||
                        (supblk.ftr_incompat & EXT3_FEATURE_INCOMPAT_RECOVER) == EXT3_FEATURE_INCOMPAT_RECOVER ||
                        (supblk.ftr_incompat & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) ==
                        EXT3_FEATURE_INCOMPAT_JOURNAL_DEV;

                if ((supblk.ftr_ro_compat & EXT4_FEATURE_RO_COMPAT_HUGE_FILE) == EXT4_FEATURE_RO_COMPAT_HUGE_FILE ||
                    (supblk.ftr_ro_compat & EXT4_FEATURE_RO_COMPAT_GDT_CSUM) == EXT4_FEATURE_RO_COMPAT_GDT_CSUM ||
                    (supblk.ftr_ro_compat & EXT4_FEATURE_RO_COMPAT_DIR_NLINK) == EXT4_FEATURE_RO_COMPAT_DIR_NLINK ||
                    (supblk.ftr_ro_compat & EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE) ==
                    EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE ||
                    (supblk.ftr_incompat & EXT4_FEATURE_INCOMPAT_64BIT) == EXT4_FEATURE_INCOMPAT_64BIT ||
                    (supblk.ftr_incompat & EXT4_FEATURE_INCOMPAT_MMP) == EXT4_FEATURE_INCOMPAT_MMP ||
                    (supblk.ftr_incompat & EXT4_FEATURE_INCOMPAT_FLEX_BG) == EXT4_FEATURE_INCOMPAT_FLEX_BG ||
                    (supblk.ftr_incompat & EXT4_FEATURE_INCOMPAT_EA_INODE) == EXT4_FEATURE_INCOMPAT_EA_INODE ||
                    (supblk.ftr_incompat & EXT4_FEATURE_INCOMPAT_DIRDATA) == EXT4_FEATURE_INCOMPAT_DIRDATA)
                {
                    ext3 = false;
                    ext4 = true;
                }

                newExt2 |= !ext3 && !ext4;

                if (newExt2)
                {
                    sb.AppendLine("ext2 filesystem");
                    XmlFsType.Type = "ext2";
                }

                if (ext3)
                {
                    sb.AppendLine("ext3 filesystem");
                    XmlFsType.Type = "ext3";
                }

                if (ext4)
                {
                    sb.AppendLine("ext4 filesystem");
                    XmlFsType.Type = "ext4";
                }

                break;

            default:
                information = "Not a ext2/3/4 filesystem" + Environment.NewLine;

                return;
            }

            string extOs;

            switch (supblk.creator_os)
            {
            case EXT2_OS_FREEBSD:
                extOs = "FreeBSD";

                break;

            case EXT2_OS_HURD:
                extOs = "Hurd";

                break;

            case EXT2_OS_LINUX:
                extOs = "Linux";

                break;

            case EXT2_OS_LITES:
                extOs = "Lites";

                break;

            case EXT2_OS_MASIX:
                extOs = "MasIX";

                break;

            default:
                extOs = $"Unknown OS ({supblk.creator_os})";

                break;
            }

            XmlFsType.SystemIdentifier = extOs;

            if (supblk.mkfs_t > 0)
            {
                sb.AppendFormat("Volume was created on {0} for {1}", DateHandlers.UnixUnsignedToDateTime(supblk.mkfs_t),
                                extOs).AppendLine();

                XmlFsType.CreationDate          = DateHandlers.UnixUnsignedToDateTime(supblk.mkfs_t);
                XmlFsType.CreationDateSpecified = true;
            }
            else
            {
                sb.AppendFormat("Volume was created for {0}", extOs).AppendLine();
            }

            byte[] tempBytes = new byte[8];
            ulong  blocks, reserved, free;

            if ((supblk.ftr_incompat & EXT4_FEATURE_INCOMPAT_64BIT) == EXT4_FEATURE_INCOMPAT_64BIT)
            {
                byte[] tempLo = BitConverter.GetBytes(supblk.blocks);
                byte[] tempHi = BitConverter.GetBytes(supblk.blocks_hi);
                tempBytes[0] = tempLo[0];
                tempBytes[1] = tempLo[1];
                tempBytes[2] = tempLo[2];
                tempBytes[3] = tempLo[3];
                tempBytes[4] = tempHi[0];
                tempBytes[5] = tempHi[1];
                tempBytes[6] = tempHi[2];
                tempBytes[7] = tempHi[3];
                blocks       = BitConverter.ToUInt64(tempBytes, 0);

                tempLo       = BitConverter.GetBytes(supblk.reserved_blocks);
                tempHi       = BitConverter.GetBytes(supblk.reserved_blocks_hi);
                tempBytes[0] = tempLo[0];
                tempBytes[1] = tempLo[1];
                tempBytes[2] = tempLo[2];
                tempBytes[3] = tempLo[3];
                tempBytes[4] = tempHi[0];
                tempBytes[5] = tempHi[1];
                tempBytes[6] = tempHi[2];
                tempBytes[7] = tempHi[3];
                reserved     = BitConverter.ToUInt64(tempBytes, 0);

                tempLo       = BitConverter.GetBytes(supblk.free_blocks);
                tempHi       = BitConverter.GetBytes(supblk.free_blocks_hi);
                tempBytes[0] = tempLo[0];
                tempBytes[1] = tempLo[1];
                tempBytes[2] = tempLo[2];
                tempBytes[3] = tempLo[3];
                tempBytes[4] = tempHi[0];
                tempBytes[5] = tempHi[1];
                tempBytes[6] = tempHi[2];
                tempBytes[7] = tempHi[3];
                free         = BitConverter.ToUInt64(tempBytes, 0);
            }
            else
            {
                blocks   = supblk.blocks;
                reserved = supblk.reserved_blocks;
                free     = supblk.free_blocks;
            }

            if (supblk.block_size == 0) // Then it is 1024 bytes
            {
                supblk.block_size = 1024;
            }

            sb.AppendFormat("Volume has {0} blocks of {1} bytes, for a total of {2} bytes", blocks,
                            1024 << (int)supblk.block_size, blocks * (ulong)(1024 << (int)supblk.block_size)).
            AppendLine();

            XmlFsType.Clusters    = blocks;
            XmlFsType.ClusterSize = (uint)(1024 << (int)supblk.block_size);

            if (supblk.mount_t > 0 ||
                supblk.mount_c > 0)
            {
                if (supblk.mount_t > 0)
                {
                    sb.AppendFormat("Last mounted on {0}", DateHandlers.UnixUnsignedToDateTime(supblk.mount_t)).
                    AppendLine();
                }

                if (supblk.max_mount_c != -1)
                {
                    sb.AppendFormat("Volume has been mounted {0} times of a maximum of {1} mounts before checking",
                                    supblk.mount_c, supblk.max_mount_c).AppendLine();
                }
                else
                {
                    sb.AppendFormat("Volume has been mounted {0} times with no maximum no. of mounts before checking",
                                    supblk.mount_c).AppendLine();
                }

                if (!string.IsNullOrEmpty(StringHandlers.CToString(supblk.last_mount_dir, Encoding)))
                {
                    sb.AppendFormat("Last mounted on: \"{0}\"",
                                    StringHandlers.CToString(supblk.last_mount_dir, Encoding)).AppendLine();
                }

                if (!string.IsNullOrEmpty(StringHandlers.CToString(supblk.mount_options, Encoding)))
                {
                    sb.AppendFormat("Last used mount options were: {0}",
                                    StringHandlers.CToString(supblk.mount_options, Encoding)).AppendLine();
                }
            }
            else
            {
                sb.AppendLine("Volume has never been mounted");

                if (supblk.max_mount_c != -1)
                {
                    sb.AppendFormat("Volume can be mounted {0} times before checking", supblk.max_mount_c).AppendLine();
                }
                else
                {
                    sb.AppendLine("Volume has no maximum no. of mounts before checking");
                }
            }

            if (supblk.check_t > 0)
            {
                if (supblk.check_inv > 0)
                {
                    sb.AppendFormat("Last checked on {0} (should check every {1} seconds)",
                                    DateHandlers.UnixUnsignedToDateTime(supblk.check_t), supblk.check_inv).AppendLine();
                }
                else
                {
                    sb.AppendFormat("Last checked on {0}", DateHandlers.UnixUnsignedToDateTime(supblk.check_t)).
                    AppendLine();
                }
            }
            else
            {
                if (supblk.check_inv > 0)
                {
                    sb.AppendFormat("Volume has never been checked (should check every {0})", supblk.check_inv).
                    AppendLine();
                }
                else
                {
                    sb.AppendLine("Volume has never been checked");
                }
            }

            if (supblk.write_t > 0)
            {
                sb.AppendFormat("Last written on {0}", DateHandlers.UnixUnsignedToDateTime(supblk.write_t)).
                AppendLine();

                XmlFsType.ModificationDate          = DateHandlers.UnixUnsignedToDateTime(supblk.write_t);
                XmlFsType.ModificationDateSpecified = true;
            }
            else
            {
                sb.AppendLine("Volume has never been written");
            }

            XmlFsType.Dirty = true;

            switch (supblk.state)
            {
            case EXT2_VALID_FS:
                sb.AppendLine("Volume is clean");
                XmlFsType.Dirty = false;

                break;

            case EXT2_ERROR_FS:
                sb.AppendLine("Volume is dirty");

                break;

            case EXT3_ORPHAN_FS:
                sb.AppendLine("Volume is recovering orphan files");

                break;

            default:
                sb.AppendFormat("Volume is in an unknown state ({0})", supblk.state).AppendLine();

                break;
            }

            if (!string.IsNullOrEmpty(StringHandlers.CToString(supblk.volume_name, Encoding)))
            {
                sb.AppendFormat("Volume name: \"{0}\"", StringHandlers.CToString(supblk.volume_name, Encoding)).
                AppendLine();

                XmlFsType.VolumeName = StringHandlers.CToString(supblk.volume_name, Encoding);
            }

            switch (supblk.err_behaviour)
            {
            case EXT2_ERRORS_CONTINUE:
                sb.AppendLine("On errors, filesystem should continue");

                break;

            case EXT2_ERRORS_RO:
                sb.AppendLine("On errors, filesystem should remount read-only");

                break;

            case EXT2_ERRORS_PANIC:
                sb.AppendLine("On errors, filesystem should panic");

                break;

            default:
                sb.AppendFormat("On errors filesystem will do an unknown thing ({0})", supblk.err_behaviour).
                AppendLine();

                break;
            }

            if (supblk.revision > 0)
            {
                sb.AppendFormat("Filesystem revision: {0}.{1}", supblk.revision, supblk.minor_revision).AppendLine();
            }

            if (supblk.uuid != Guid.Empty)
            {
                sb.AppendFormat("Volume UUID: {0}", supblk.uuid).AppendLine();
                XmlFsType.VolumeSerial = supblk.uuid.ToString();
            }

            if (supblk.kbytes_written > 0)
            {
                sb.AppendFormat("{0} KiB has been written on volume", supblk.kbytes_written).AppendLine();
            }

            sb.AppendFormat("{0} reserved and {1} free blocks", reserved, free).AppendLine();
            XmlFsType.FreeClusters          = free;
            XmlFsType.FreeClustersSpecified = true;

            sb.AppendFormat("{0} inodes with {1} free inodes ({2}%)", supblk.inodes, supblk.free_inodes,
                            (supblk.free_inodes * 100) / supblk.inodes).AppendLine();

            if (supblk.first_inode > 0)
            {
                sb.AppendFormat("First inode is {0}", supblk.first_inode).AppendLine();
            }

            if (supblk.frag_size > 0)
            {
                sb.AppendFormat("{0} bytes per fragment", supblk.frag_size).AppendLine();
            }

            if (supblk.blocks_per_grp > 0 &&
                supblk.flags_per_grp > 0 &&
                supblk.inodes_per_grp > 0)
            {
                sb.AppendFormat("{0} blocks, {1} flags and {2} inodes per group", supblk.blocks_per_grp,
                                supblk.flags_per_grp, supblk.inodes_per_grp).AppendLine();
            }

            if (supblk.first_block > 0)
            {
                sb.AppendFormat("{0} is first data block", supblk.first_block).AppendLine();
            }

            sb.AppendFormat("Default UID: {0}, GID: {1}", supblk.default_uid, supblk.default_gid).AppendLine();

            if (supblk.block_group_no > 0)
            {
                sb.AppendFormat("Block group number is {0}", supblk.block_group_no).AppendLine();
            }

            if (supblk.desc_grp_size > 0)
            {
                sb.AppendFormat("Group descriptor size is {0} bytes", supblk.desc_grp_size).AppendLine();
            }

            if (supblk.first_meta_bg > 0)
            {
                sb.AppendFormat("First metablock group is {0}", supblk.first_meta_bg).AppendLine();
            }

            if (supblk.raid_stride > 0)
            {
                sb.AppendFormat("RAID stride: {0}", supblk.raid_stride).AppendLine();
            }

            if (supblk.raid_stripe_width > 0)
            {
                sb.AppendFormat("{0} blocks on all data disks", supblk.raid_stripe_width).AppendLine();
            }

            if (supblk.mmp_interval > 0 &&
                supblk.mmp_block > 0)
            {
                sb.AppendFormat("{0} seconds for multi-mount protection wait, on block {1}", supblk.mmp_interval,
                                supblk.mmp_block).AppendLine();
            }

            if (supblk.flex_bg_grp_size > 0)
            {
                sb.AppendFormat("{0} Flexible block group size", supblk.flex_bg_grp_size).AppendLine();
            }

            if (supblk.hash_seed_1 > 0 &&
                supblk.hash_seed_2 > 0 &&
                supblk.hash_seed_3 > 0 &&
                supblk.hash_seed_4 > 0)
            {
                sb.AppendFormat("Hash seed: {0:X8}{1:X8}{2:X8}{3:X8}, version {4}", supblk.hash_seed_1,
                                supblk.hash_seed_2, supblk.hash_seed_3, supblk.hash_seed_4, supblk.hash_version).
                AppendLine();
            }

            if ((supblk.ftr_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL) == EXT3_FEATURE_COMPAT_HAS_JOURNAL ||
                (supblk.ftr_incompat & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) == EXT3_FEATURE_INCOMPAT_JOURNAL_DEV)
            {
                sb.AppendLine("Volume is journaled");

                if (supblk.journal_uuid != Guid.Empty)
                {
                    sb.AppendFormat("Journal UUID: {0}", supblk.journal_uuid).AppendLine();
                }

                sb.AppendFormat("Journal has inode {0}", supblk.journal_inode).AppendLine();

                if ((supblk.ftr_compat & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) == EXT3_FEATURE_INCOMPAT_JOURNAL_DEV &&
                    supblk.journal_dev > 0)
                {
                    sb.AppendFormat("Journal is on device {0}", supblk.journal_dev).AppendLine();
                }

                if (supblk.jnl_backup_type > 0)
                {
                    sb.AppendFormat("Journal backup type: {0}", supblk.jnl_backup_type).AppendLine();
                }

                if (supblk.last_orphan > 0)
                {
                    sb.AppendFormat("Last orphaned inode is {0}", supblk.last_orphan).AppendLine();
                }
                else
                {
                    sb.AppendLine("There are no orphaned inodes");
                }
            }

            if (ext4)
            {
                if (supblk.snapshot_id > 0)
                {
                    sb.
                    AppendFormat("Active snapshot has ID {0}, on inode {1}, with {2} blocks reserved, list starting on block {3}",
                                 supblk.snapshot_id, supblk.snapshot_inum, supblk.snapshot_blocks,
                                 supblk.snapshot_list).AppendLine();
                }

                if (supblk.error_count > 0)
                {
                    sb.AppendFormat("{0} errors registered", supblk.error_count).AppendLine();

                    sb.AppendFormat("First error occurred on {0}, last on {1}",
                                    DateHandlers.UnixUnsignedToDateTime(supblk.first_error_t),
                                    DateHandlers.UnixUnsignedToDateTime(supblk.last_error_t)).AppendLine();

                    sb.AppendFormat("First error inode is {0}, last is {1}", supblk.first_error_inode,
                                    supblk.last_error_inode).AppendLine();

                    sb.AppendFormat("First error block is {0}, last is {1}", supblk.first_error_block,
                                    supblk.last_error_block).AppendLine();

                    sb.AppendFormat("First error function is \"{0}\", last is \"{1}\"", supblk.first_error_func,
                                    supblk.last_error_func).AppendLine();
                }
            }

            sb.AppendFormat("Flags…:").AppendLine();

            if ((supblk.flags & EXT2_FLAGS_SIGNED_HASH) == EXT2_FLAGS_SIGNED_HASH)
            {
                sb.AppendLine("Signed directory hash is in use");
            }

            if ((supblk.flags & EXT2_FLAGS_UNSIGNED_HASH) == EXT2_FLAGS_UNSIGNED_HASH)
            {
                sb.AppendLine("Unsigned directory hash is in use");
            }

            if ((supblk.flags & EXT2_FLAGS_TEST_FILESYS) == EXT2_FLAGS_TEST_FILESYS)
            {
                sb.AppendLine("Volume is testing development code");
            }

            if ((supblk.flags & 0xFFFFFFF8) != 0)
            {
                sb.AppendFormat("Unknown set flags: {0:X8}", supblk.flags);
            }

            sb.AppendLine();

            sb.AppendFormat("Default mount options…:").AppendLine();

            if ((supblk.default_mnt_opts & EXT2_DEFM_DEBUG) == EXT2_DEFM_DEBUG)
            {
                sb.AppendLine("(debug): Enable debugging code");
            }

            if ((supblk.default_mnt_opts & EXT2_DEFM_BSDGROUPS) == EXT2_DEFM_BSDGROUPS)
            {
                sb.AppendLine("(bsdgroups): Emulate BSD behaviour when creating new files");
            }

            if ((supblk.default_mnt_opts & EXT2_DEFM_XATTR_USER) == EXT2_DEFM_XATTR_USER)
            {
                sb.AppendLine("(user_xattr): Enable user-specified extended attributes");
            }

            if ((supblk.default_mnt_opts & EXT2_DEFM_ACL) == EXT2_DEFM_ACL)
            {
                sb.AppendLine("(acl): Enable POSIX ACLs");
            }

            if ((supblk.default_mnt_opts & EXT2_DEFM_UID16) == EXT2_DEFM_UID16)
            {
                sb.AppendLine("(uid16): Disable 32bit UIDs and GIDs");
            }

            if ((supblk.default_mnt_opts & EXT3_DEFM_JMODE_DATA) == EXT3_DEFM_JMODE_DATA)
            {
                sb.AppendLine("(journal_data): Journal data and metadata");
            }

            if ((supblk.default_mnt_opts & EXT3_DEFM_JMODE_ORDERED) == EXT3_DEFM_JMODE_ORDERED)
            {
                sb.AppendLine("(journal_data_ordered): Write data before journaling metadata");
            }

            if ((supblk.default_mnt_opts & EXT3_DEFM_JMODE_WBACK) == EXT3_DEFM_JMODE_WBACK)
            {
                sb.AppendLine("(journal_data_writeback): Write journal before data");
            }

            if ((supblk.default_mnt_opts & 0xFFFFFE20) != 0)
            {
                sb.AppendFormat("Unknown set default mount options: {0:X8}", supblk.default_mnt_opts);
            }

            sb.AppendLine();

            sb.AppendFormat("Compatible features…:").AppendLine();

            if ((supblk.ftr_compat & EXT2_FEATURE_COMPAT_DIR_PREALLOC) == EXT2_FEATURE_COMPAT_DIR_PREALLOC)
            {
                sb.AppendLine("Pre-allocate directories");
            }

            if ((supblk.ftr_compat & EXT2_FEATURE_COMPAT_IMAGIC_INODES) == EXT2_FEATURE_COMPAT_IMAGIC_INODES)
            {
                sb.AppendLine("imagic inodes ?");
            }

            if ((supblk.ftr_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL) == EXT3_FEATURE_COMPAT_HAS_JOURNAL)
            {
                sb.AppendLine("Has journal (ext3)");
            }

            if ((supblk.ftr_compat & EXT2_FEATURE_COMPAT_EXT_ATTR) == EXT2_FEATURE_COMPAT_EXT_ATTR)
            {
                sb.AppendLine("Has extended attribute blocks");
            }

            if ((supblk.ftr_compat & EXT2_FEATURE_COMPAT_RESIZE_INO) == EXT2_FEATURE_COMPAT_RESIZE_INO)
            {
                sb.AppendLine("Has online filesystem resize reservations");
            }

            if ((supblk.ftr_compat & EXT2_FEATURE_COMPAT_DIR_INDEX) == EXT2_FEATURE_COMPAT_DIR_INDEX)
            {
                sb.AppendLine("Can use hashed indexes on directories");
            }

            if ((supblk.ftr_compat & 0xFFFFFFC0) != 0)
            {
                sb.AppendFormat("Unknown compatible features: {0:X8}", supblk.ftr_compat);
            }

            sb.AppendLine();

            sb.AppendFormat("Compatible features if read-only…:").AppendLine();

            if ((supblk.ftr_ro_compat & EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER) == EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER)
            {
                sb.AppendLine("Reduced number of superblocks");
            }

            if ((supblk.ftr_ro_compat & EXT2_FEATURE_RO_COMPAT_LARGE_FILE) == EXT2_FEATURE_RO_COMPAT_LARGE_FILE)
            {
                sb.AppendLine("Can have files bigger than 2GiB");
            }

            if ((supblk.ftr_ro_compat & EXT2_FEATURE_RO_COMPAT_BTREE_DIR) == EXT2_FEATURE_RO_COMPAT_BTREE_DIR)
            {
                sb.AppendLine("Uses B-Tree for directories");
            }

            if ((supblk.ftr_ro_compat & EXT4_FEATURE_RO_COMPAT_HUGE_FILE) == EXT4_FEATURE_RO_COMPAT_HUGE_FILE)
            {
                sb.AppendLine("Can have files bigger than 2TiB (ext4)");
            }

            if ((supblk.ftr_ro_compat & EXT4_FEATURE_RO_COMPAT_GDT_CSUM) == EXT4_FEATURE_RO_COMPAT_GDT_CSUM)
            {
                sb.AppendLine("Group descriptor checksums and sparse inode table (ext4)");
            }

            if ((supblk.ftr_ro_compat & EXT4_FEATURE_RO_COMPAT_DIR_NLINK) == EXT4_FEATURE_RO_COMPAT_DIR_NLINK)
            {
                sb.AppendLine("More than 32000 directory entries (ext4)");
            }

            if ((supblk.ftr_ro_compat & EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE) == EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE)
            {
                sb.AppendLine("Supports nanosecond timestamps and creation time (ext4)");
            }

            if ((supblk.ftr_ro_compat & 0xFFFFFF80) != 0)
            {
                sb.AppendFormat("Unknown read-only compatible features: {0:X8}", supblk.ftr_ro_compat);
            }

            sb.AppendLine();

            sb.AppendFormat("Incompatible features…:").AppendLine();

            if ((supblk.ftr_incompat & EXT2_FEATURE_INCOMPAT_COMPRESSION) == EXT2_FEATURE_INCOMPAT_COMPRESSION)
            {
                sb.AppendLine("Uses compression");
            }

            if ((supblk.ftr_incompat & EXT2_FEATURE_INCOMPAT_FILETYPE) == EXT2_FEATURE_INCOMPAT_FILETYPE)
            {
                sb.AppendLine("Filetype in directory entries");
            }

            if ((supblk.ftr_incompat & EXT3_FEATURE_INCOMPAT_RECOVER) == EXT3_FEATURE_INCOMPAT_RECOVER)
            {
                sb.AppendLine("Journal needs recovery (ext3)");
            }

            if ((supblk.ftr_incompat & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) == EXT3_FEATURE_INCOMPAT_JOURNAL_DEV)
            {
                sb.AppendLine("Has journal on another device (ext3)");
            }

            if ((supblk.ftr_incompat & EXT2_FEATURE_INCOMPAT_META_BG) == EXT2_FEATURE_INCOMPAT_META_BG)
            {
                sb.AppendLine("Reduced block group backups");
            }

            if ((supblk.ftr_incompat & EXT4_FEATURE_INCOMPAT_EXTENTS) == EXT4_FEATURE_INCOMPAT_EXTENTS)
            {
                sb.AppendLine("Volume use extents (ext4)");
            }

            if ((supblk.ftr_incompat & EXT4_FEATURE_INCOMPAT_64BIT) == EXT4_FEATURE_INCOMPAT_64BIT)
            {
                sb.AppendLine("Supports volumes bigger than 2^32 blocks (ext4)");
            }

            if ((supblk.ftr_incompat & EXT4_FEATURE_INCOMPAT_MMP) == EXT4_FEATURE_INCOMPAT_MMP)
            {
                sb.AppendLine("Multi-mount protection (ext4)");
            }

            if ((supblk.ftr_incompat & EXT4_FEATURE_INCOMPAT_FLEX_BG) == EXT4_FEATURE_INCOMPAT_FLEX_BG)
            {
                sb.AppendLine("Flexible block group metadata location (ext4)");
            }

            if ((supblk.ftr_incompat & EXT4_FEATURE_INCOMPAT_EA_INODE) == EXT4_FEATURE_INCOMPAT_EA_INODE)
            {
                sb.AppendLine("Extended attributes can reside in inode (ext4)");
            }

            if ((supblk.ftr_incompat & EXT4_FEATURE_INCOMPAT_DIRDATA) == EXT4_FEATURE_INCOMPAT_DIRDATA)
            {
                sb.AppendLine("Data can reside in directory entry (ext4)");
            }

            if ((supblk.ftr_incompat & 0xFFFFF020) != 0)
            {
                sb.AppendFormat("Unknown incompatible features: {0:X8}", supblk.ftr_incompat);
            }

            information = sb.ToString();
        }
Пример #16
0
        public Errno Mount(IMediaImage imagePlugin, Partition partition, Encoding encoding,
                           Dictionary <string, string> options, string @namespace)
        {
            Encoding     = Encoding.GetEncoding("iso-8859-15");
            littleEndian = true;
            if (options == null)
            {
                options = GetDefaultOptions();
            }
            if (options.TryGetValue("debug", out string debugString))
            {
                bool.TryParse(debugString, out debug);
            }

            if (imagePlugin.Info.SectorSize < 512)
            {
                return(Errno.InvalidArgument);
            }

            DicConsole.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);
            }

            DicConsole.DebugWriteLine("Xbox FAT plugin",
                                      littleEndian ? "Filesystem is little endian" : "Filesystem is big endian");

            int logicalSectorsPerPhysicalSectors = partition.Offset == 0 && littleEndian ? 8 : 1;

            DicConsole.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
            };

            DicConsole.DebugWriteLine("Xbox FAT plugin", "XmlFsType.ClusterSize: {0}", XmlFsType.ClusterSize);
            DicConsole.DebugWriteLine("Xbox FAT plugin", "XmlFsType.VolumeName: {0}", XmlFsType.VolumeName);
            DicConsole.DebugWriteLine("Xbox FAT plugin", "XmlFsType.VolumeSerial: {0}", XmlFsType.VolumeSerial);
            DicConsole.DebugWriteLine("Xbox FAT plugin", "stat.Blocks: {0}", statfs.Blocks);
            DicConsole.DebugWriteLine("Xbox FAT plugin", "stat.FilenameLength: {0}", statfs.FilenameLength);
            DicConsole.DebugWriteLine("Xbox FAT plugin", "stat.Id: {0}", statfs.Id.Serial32);
            DicConsole.DebugWriteLine("Xbox FAT plugin", "stat.Type: {0}", statfs.Type);

            byte[] buffer;
            fatStartSector = FAT_START / imagePlugin.Info.SectorSize + partition.Start;
            uint fatSize;

            DicConsole.DebugWriteLine("Xbox FAT plugin", "fatStartSector: {0}", fatStartSector);

            if (statfs.Blocks > MAX_XFAT16_CLUSTERS)
            {
                DicConsole.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);

                DicConsole.DebugWriteLine("Xbox FAT plugin", "FAT is {0} sectors", fatSize);

                buffer = imagePlugin.ReadSectors(fatStartSector, fatSize);

                DicConsole.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]);
                    }
                }

                DicConsole.DebugWriteLine("Xbox FAT plugin", "fat32[0] == FATX32_ID = {0}", fat32[0] == FATX32_ID);
                if (fat32[0] != FATX32_ID)
                {
                    return(Errno.InvalidArgument);
                }
            }
            else
            {
                DicConsole.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);

                DicConsole.DebugWriteLine("Xbox FAT plugin", "FAT is {0} sectors", fatSize);

                buffer = imagePlugin.ReadSectors(fatStartSector, fatSize);

                DicConsole.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]);
                    }
                }

                DicConsole.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);
            this.imagePlugin   = imagePlugin;
            firstClusterSector = fatStartSector + fatSize;
            bytesPerCluster    = sectorsPerCluster * imagePlugin.Info.SectorSize;

            DicConsole.DebugWriteLine("Xbox FAT plugin", "sectorsPerCluster = {0}", sectorsPerCluster);
            DicConsole.DebugWriteLine("Xbox FAT plugin", "bytesPerCluster = {0}", bytesPerCluster);
            DicConsole.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];

            DicConsole.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);
        }
Пример #17
0
        public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information,
                                   Encoding encoding)
        {
            Encoding    = encoding ?? Encoding.GetEncoding("iso-8859-15");
            information = "";
            if (imagePlugin.Info.SectorSize < 512)
            {
                return;
            }

            uint sbSize = (uint)(Marshal.SizeOf <UNICOS_Superblock>() / imagePlugin.Info.SectorSize);

            if (Marshal.SizeOf <UNICOS_Superblock>() % imagePlugin.Info.SectorSize != 0)
            {
                sbSize++;
            }

            byte[] sector = imagePlugin.ReadSectors(partition.Start, sbSize);
            if (sector.Length < Marshal.SizeOf <UNICOS_Superblock>())
            {
                return;
            }

            UNICOS_Superblock unicosSb = Marshal.ByteArrayToStructureBigEndian <UNICOS_Superblock>(sector);

            if (unicosSb.s_magic != UNICOS_MAGIC)
            {
                return;
            }

            StringBuilder sb = new StringBuilder();

            sb.AppendLine("UNICOS filesystem");
            if (unicosSb.s_secure == UNICOS_SECURE)
            {
                sb.AppendLine("Volume is secure");
            }
            sb.AppendFormat("Volume contains {0} partitions", unicosSb.s_npart).AppendLine();
            sb.AppendFormat("{0} bytes per sector", unicosSb.s_iounit).AppendLine();
            sb.AppendLine("4096 bytes per block");
            sb.AppendFormat("{0} data blocks in volume", unicosSb.s_fsize).AppendLine();
            sb.AppendFormat("Root resides on inode {0}", unicosSb.s_root).AppendLine();
            sb.AppendFormat("{0} inodes in volume", unicosSb.s_isize).AppendLine();
            sb.AppendFormat("Volume last updated on {0}", DateHandlers.UnixToDateTime(unicosSb.s_time)).AppendLine();
            if (unicosSb.s_error > 0)
            {
                sb.AppendFormat("Volume is dirty, error code = 0x{0:X16}", unicosSb.s_error).AppendLine();
            }
            sb.AppendFormat("Volume name: {0}", StringHandlers.CToString(unicosSb.s_fname, Encoding)).AppendLine();

            information = sb.ToString();

            XmlFsType = new FileSystemType
            {
                Type                      = "UNICOS filesystem",
                ClusterSize               = 4096,
                Clusters                  = (ulong)unicosSb.s_fsize,
                VolumeName                = StringHandlers.CToString(unicosSb.s_fname, Encoding),
                ModificationDate          = DateHandlers.UnixToDateTime(unicosSb.s_time),
                ModificationDateSpecified = true
            };
            XmlFsType.Dirty |= unicosSb.s_error > 0;
        }
Пример #18
0
        public bool GetInformation(IMediaImage imagePlugin, out List <Partition> partitions, ulong sectorOffset)
        {
            partitions = new List <Partition>();
            uint run = (MAX_LABEL_SIZE + labelOffsets.Last()) / imagePlugin.Info.SectorSize;

            if ((MAX_LABEL_SIZE + labelOffsets.Last()) % imagePlugin.Info.SectorSize > 0)
            {
                run++;
            }

            var  dl    = new DiskLabel();
            bool found = false;

            foreach (ulong location in labelLocations)
            {
                if (location + run + sectorOffset >= imagePlugin.Info.Sectors)
                {
                    return(false);
                }

                byte[] tmp = imagePlugin.ReadSectors(location + sectorOffset, run);

                foreach (uint offset in labelOffsets)
                {
                    byte[] sector = new byte[MAX_LABEL_SIZE];

                    if (offset + MAX_LABEL_SIZE > tmp.Length)
                    {
                        break;
                    }

                    Array.Copy(tmp, offset, sector, 0, MAX_LABEL_SIZE);
                    dl = Marshal.ByteArrayToStructureLittleEndian <DiskLabel>(sector);

                    AaruConsole.DebugWriteLine("BSD plugin",
                                               "dl.magic on sector {0} at offset {1} = 0x{2:X8} (expected 0x{3:X8})",
                                               location + sectorOffset, offset, dl.d_magic, DISKMAGIC);

                    if ((dl.d_magic != DISKMAGIC || dl.d_magic2 != DISKMAGIC) &&
                        (dl.d_magic != DISKCIGAM || dl.d_magic2 != DISKCIGAM))
                    {
                        continue;
                    }

                    found = true;

                    break;
                }

                if (found)
                {
                    break;
                }
            }

            if (!found)
            {
                return(false);
            }

            if (dl.d_magic == DISKCIGAM &&
                dl.d_magic2 == DISKCIGAM)
            {
                dl = SwapDiskLabel(dl);
            }

            AaruConsole.DebugWriteLine("BSD plugin", "dl.d_type = {0}", dl.d_type);
            AaruConsole.DebugWriteLine("BSD plugin", "dl.d_subtype = {0}", dl.d_subtype);
            AaruConsole.DebugWriteLine("BSD plugin", "dl.d_typename = {0}", StringHandlers.CToString(dl.d_typename));
            AaruConsole.DebugWriteLine("BSD plugin", "dl.d_packname = {0}", StringHandlers.CToString(dl.d_packname));
            AaruConsole.DebugWriteLine("BSD plugin", "dl.d_secsize = {0}", dl.d_secsize);
            AaruConsole.DebugWriteLine("BSD plugin", "dl.d_nsectors = {0}", dl.d_nsectors);
            AaruConsole.DebugWriteLine("BSD plugin", "dl.d_ntracks = {0}", dl.d_ntracks);
            AaruConsole.DebugWriteLine("BSD plugin", "dl.d_ncylinders = {0}", dl.d_ncylinders);
            AaruConsole.DebugWriteLine("BSD plugin", "dl.d_secpercyl = {0}", dl.d_secpercyl);
            AaruConsole.DebugWriteLine("BSD plugin", "dl.d_secperunit = {0}", dl.d_secperunit);
            AaruConsole.DebugWriteLine("BSD plugin", "dl.d_sparespertrack = {0}", dl.d_sparespertrack);
            AaruConsole.DebugWriteLine("BSD plugin", "dl.d_sparespercyl = {0}", dl.d_sparespercyl);
            AaruConsole.DebugWriteLine("BSD plugin", "dl.d_acylinders = {0}", dl.d_acylinders);
            AaruConsole.DebugWriteLine("BSD plugin", "dl.d_rpm = {0}", dl.d_rpm);
            AaruConsole.DebugWriteLine("BSD plugin", "dl.d_interleave = {0}", dl.d_interleave);
            AaruConsole.DebugWriteLine("BSD plugin", "dl.d_trackskew = {0}", dl.d_trackskew);
            AaruConsole.DebugWriteLine("BSD plugin", "dl.d_cylskeew = {0}", dl.d_cylskeew);
            AaruConsole.DebugWriteLine("BSD plugin", "dl.d_headswitch = {0}", dl.d_headswitch);
            AaruConsole.DebugWriteLine("BSD plugin", "dl.d_trkseek = {0}", dl.d_trkseek);
            AaruConsole.DebugWriteLine("BSD plugin", "dl.d_flags = {0}", dl.d_flags);
            AaruConsole.DebugWriteLine("BSD plugin", "dl.d_drivedata[0] = {0}", dl.d_drivedata[0]);
            AaruConsole.DebugWriteLine("BSD plugin", "dl.d_drivedata[1] = {0}", dl.d_drivedata[1]);
            AaruConsole.DebugWriteLine("BSD plugin", "dl.d_drivedata[2] = {0}", dl.d_drivedata[2]);
            AaruConsole.DebugWriteLine("BSD plugin", "dl.d_drivedata[3] = {0}", dl.d_drivedata[3]);
            AaruConsole.DebugWriteLine("BSD plugin", "dl.d_drivedata[4] = {0}", dl.d_drivedata[4]);
            AaruConsole.DebugWriteLine("BSD plugin", "dl.d_spare[0] = {0}", dl.d_spare[0]);
            AaruConsole.DebugWriteLine("BSD plugin", "dl.d_spare[1] = {0}", dl.d_spare[1]);
            AaruConsole.DebugWriteLine("BSD plugin", "dl.d_spare[2] = {0}", dl.d_spare[2]);
            AaruConsole.DebugWriteLine("BSD plugin", "dl.d_spare[3] = {0}", dl.d_spare[3]);
            AaruConsole.DebugWriteLine("BSD plugin", "dl.d_spare[4] = {0}", dl.d_spare[4]);
            AaruConsole.DebugWriteLine("BSD plugin", "dl.d_magic2 = 0x{0:X8}", dl.d_magic2);
            AaruConsole.DebugWriteLine("BSD plugin", "dl.d_checksum = 0x{0:X8}", dl.d_checksum);
            AaruConsole.DebugWriteLine("BSD plugin", "dl.d_npartitions = {0}", dl.d_npartitions);
            AaruConsole.DebugWriteLine("BSD plugin", "dl.d_bbsize = {0}", dl.d_bbsize);
            AaruConsole.DebugWriteLine("BSD plugin", "dl.d_sbsize = {0}", dl.d_sbsize);

            ulong counter         = 0;
            bool  addSectorOffset = false;

            for (int i = 0; i < dl.d_npartitions && i < 22; i++)
            {
                AaruConsole.DebugWriteLine("BSD plugin", "dl.d_partitions[i].p_offset = {0}",
                                           dl.d_partitions[i].p_offset);

                AaruConsole.DebugWriteLine("BSD plugin", "dl.d_partitions[i].p_size = {0}", dl.d_partitions[i].p_size);

                AaruConsole.DebugWriteLine("BSD plugin", "dl.d_partitions[i].p_fstype = {0} ({1})",
                                           dl.d_partitions[i].p_fstype, fsTypeToString(dl.d_partitions[i].p_fstype));

                var part = new Partition
                {
                    Start    = (dl.d_partitions[i].p_offset * dl.d_secsize) / imagePlugin.Info.SectorSize,
                    Offset   = dl.d_partitions[i].p_offset * dl.d_secsize,
                    Length   = (dl.d_partitions[i].p_size * dl.d_secsize) / imagePlugin.Info.SectorSize,
                    Size     = dl.d_partitions[i].p_size * dl.d_secsize,
                    Type     = fsTypeToString(dl.d_partitions[i].p_fstype),
                    Sequence = counter, Scheme = Name
                };

                if (dl.d_partitions[i].p_fstype == fsType.Unused)
                {
                    continue;
                }

                // Crude and dirty way to know if the disklabel is relative to its parent partition...
                if (dl.d_partitions[i].p_offset < sectorOffset &&
                    !addSectorOffset)
                {
                    addSectorOffset = true;
                }

                if (addSectorOffset)
                {
                    part.Start  += sectorOffset;
                    part.Offset += sectorOffset * imagePlugin.Info.SectorSize;
                }

                AaruConsole.DebugWriteLine("BSD plugin", "part.start = {0}", part.Start);
                AaruConsole.DebugWriteLine("BSD plugin", "Adding it...");
                partitions.Add(part);
                counter++;
            }

            return(partitions.Count > 0);
        }
Пример #19
0
        public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information,
                                   Encoding encoding)
        {
            Encoding = encoding ?? Encoding.GetEncoding("iso-8859-1");
            var sbInformation = new StringBuilder();

            XmlFsType   = new FileSystemType();
            information = null;
            byte[] bootBlockSectors = imagePlugin.ReadSectors(0 + partition.Start, 2);

            BootBlock bootBlk = Marshal.ByteArrayToStructureBigEndian <BootBlock>(bootBlockSectors);

            bootBlk.bootCode = new byte[bootBlockSectors.Length - 12];
            Array.Copy(bootBlockSectors, 12, bootBlk.bootCode, 0, bootBlk.bootCode.Length);
            bootBlockSectors[4] = bootBlockSectors[5] = bootBlockSectors[6] = bootBlockSectors[7] = 0;
            uint bsum = AmigaBootChecksum(bootBlockSectors);

            ulong bRootPtr = 0;

            // If bootblock is correct, let's take its rootblock pointer
            if (bsum == bootBlk.checksum)
            {
                bRootPtr = bootBlk.root_ptr + partition.Start;
                AaruConsole.DebugWriteLine("AmigaDOS plugin", "Bootblock points to {0} as Rootblock", bRootPtr);
            }

            ulong[] rootPtrs =
            {
                bRootPtr + partition.Start,                                            ((((partition.End - partition.Start) + 1) / 2) + partition.Start) - 2,
                ((((partition.End - partition.Start) + 1) / 2) + partition.Start) - 1,
                (((partition.End - partition.Start) + 1) / 2) + partition.Start,
                (((partition.End - partition.Start) + 1) / 2) + partition.Start + 4
            };

            var rootBlk = new RootBlock();

            byte[] rootBlockSector = null;

            bool rootFound = false;
            uint blockSize = 0;

            // So to handle even number of sectors
            foreach (ulong rootPtr in rootPtrs.Where(rootPtr => rootPtr < partition.End && rootPtr >= partition.Start))
            {
                AaruConsole.DebugWriteLine("AmigaDOS plugin", "Searching for Rootblock in sector {0}", rootPtr);

                rootBlockSector = imagePlugin.ReadSector(rootPtr);

                rootBlk.type = BigEndianBitConverter.ToUInt32(rootBlockSector, 0x00);
                AaruConsole.DebugWriteLine("AmigaDOS plugin", "rootBlk.type = {0}", rootBlk.type);

                if (rootBlk.type != TYPE_HEADER)
                {
                    continue;
                }

                rootBlk.hashTableSize = BigEndianBitConverter.ToUInt32(rootBlockSector, 0x0C);

                AaruConsole.DebugWriteLine("AmigaDOS plugin", "rootBlk.hashTableSize = {0}", rootBlk.hashTableSize);

                blockSize = (rootBlk.hashTableSize + 56) * 4;
                uint sectorsPerBlock = (uint)(blockSize / rootBlockSector.Length);

                AaruConsole.DebugWriteLine("AmigaDOS plugin", "blockSize = {0}", blockSize);
                AaruConsole.DebugWriteLine("AmigaDOS plugin", "sectorsPerBlock = {0}", sectorsPerBlock);

                if (blockSize % rootBlockSector.Length > 0)
                {
                    sectorsPerBlock++;
                }

                if (rootPtr + sectorsPerBlock >= partition.End)
                {
                    continue;
                }

                rootBlockSector = imagePlugin.ReadSectors(rootPtr, sectorsPerBlock);

                // Clear checksum on sector
                rootBlk.checksum    = BigEndianBitConverter.ToUInt32(rootBlockSector, 20);
                rootBlockSector[20] = rootBlockSector[21] = rootBlockSector[22] = rootBlockSector[23] = 0;
                uint rsum = AmigaChecksum(rootBlockSector);

                AaruConsole.DebugWriteLine("AmigaDOS plugin", "rootBlk.checksum = 0x{0:X8}", rootBlk.checksum);
                AaruConsole.DebugWriteLine("AmigaDOS plugin", "rsum = 0x{0:X8}", rsum);

                rootBlk.sec_type = BigEndianBitConverter.ToUInt32(rootBlockSector, rootBlockSector.Length - 4);
                AaruConsole.DebugWriteLine("AmigaDOS plugin", "rootBlk.sec_type = {0}", rootBlk.sec_type);

                if (rootBlk.sec_type != SUBTYPE_ROOT ||
                    rootBlk.checksum != rsum)
                {
                    continue;
                }

                rootBlockSector = imagePlugin.ReadSectors(rootPtr, sectorsPerBlock);
                rootFound       = true;

                break;
            }

            if (!rootFound)
            {
                return;
            }

            rootBlk = MarshalRootBlock(rootBlockSector);

            string diskName = StringHandlers.PascalToString(rootBlk.diskName, Encoding);

            switch (bootBlk.diskType & 0xFF)
            {
            case 0:
                sbInformation.Append("Amiga Original File System");
                XmlFsType.Type = "Amiga OFS";

                break;

            case 1:
                sbInformation.Append("Amiga Fast File System");
                XmlFsType.Type = "Amiga FFS";

                break;

            case 2:
                sbInformation.Append("Amiga Original File System with international characters");
                XmlFsType.Type = "Amiga OFS";

                break;

            case 3:
                sbInformation.Append("Amiga Fast File System with international characters");
                XmlFsType.Type = "Amiga FFS";

                break;

            case 4:
                sbInformation.Append("Amiga Original File System with directory cache");
                XmlFsType.Type = "Amiga OFS";

                break;

            case 5:
                sbInformation.Append("Amiga Fast File System with directory cache");
                XmlFsType.Type = "Amiga FFS";

                break;

            case 6:
                sbInformation.Append("Amiga Original File System with long filenames");
                XmlFsType.Type = "Amiga OFS2";

                break;

            case 7:
                sbInformation.Append("Amiga Fast File System with long filenames");
                XmlFsType.Type = "Amiga FFS2";

                break;
            }

            if ((bootBlk.diskType & 0x6D754600) == 0x6D754600)
            {
                sbInformation.Append(", with multi-user patches");
            }

            sbInformation.AppendLine();

            sbInformation.AppendFormat("Volume name: {0}", diskName).AppendLine();

            if (bootBlk.checksum == bsum)
            {
                var sha1Ctx = new Sha1Context();
                sha1Ctx.Update(bootBlk.bootCode);
                sbInformation.AppendLine("Volume is bootable");
                sbInformation.AppendFormat("Boot code SHA1 is {0}", sha1Ctx.End()).AppendLine();
            }

            if (rootBlk.bitmapFlag == 0xFFFFFFFF)
            {
                sbInformation.AppendLine("Volume bitmap is valid");
            }

            if (rootBlk.bitmapExtensionBlock != 0x00000000 &&
                rootBlk.bitmapExtensionBlock != 0xFFFFFFFF)
            {
                sbInformation.AppendFormat("Bitmap extension at block {0}", rootBlk.bitmapExtensionBlock).AppendLine();
            }

            if ((bootBlk.diskType & 0xFF) == 4 ||
                (bootBlk.diskType & 0xFF) == 5)
            {
                sbInformation.AppendFormat("Directory cache starts at block {0}", rootBlk.extension).AppendLine();
            }

            ulong blocks = (((partition.End - partition.Start) + 1) * imagePlugin.Info.SectorSize) / blockSize;

            sbInformation.AppendFormat("Volume block size is {0} bytes", blockSize).AppendLine();
            sbInformation.AppendFormat("Volume has {0} blocks", blocks).AppendLine();

            sbInformation.AppendFormat("Volume created on {0}",
                                       DateHandlers.AmigaToDateTime(rootBlk.cDays, rootBlk.cMins, rootBlk.cTicks)).
            AppendLine();

            sbInformation.AppendFormat("Volume last modified on {0}",
                                       DateHandlers.AmigaToDateTime(rootBlk.vDays, rootBlk.vMins, rootBlk.vTicks)).
            AppendLine();

            sbInformation.AppendFormat("Volume root directory last modified on on {0}",
                                       DateHandlers.AmigaToDateTime(rootBlk.rDays, rootBlk.rMins, rootBlk.rTicks)).
            AppendLine();

            sbInformation.AppendFormat("Root block checksum is 0x{0:X8}", rootBlk.checksum).AppendLine();
            information = sbInformation.ToString();

            XmlFsType.CreationDate = DateHandlers.AmigaToDateTime(rootBlk.cDays, rootBlk.cMins, rootBlk.cTicks);

            XmlFsType.CreationDateSpecified = true;

            XmlFsType.ModificationDate = DateHandlers.AmigaToDateTime(rootBlk.vDays, rootBlk.vMins, rootBlk.vTicks);

            XmlFsType.ModificationDateSpecified = true;
            XmlFsType.Dirty       = rootBlk.bitmapFlag != 0xFFFFFFFF;
            XmlFsType.Clusters    = blocks;
            XmlFsType.ClusterSize = blockSize;
            XmlFsType.VolumeName  = diskName;
            XmlFsType.Bootable    = bsum == bootBlk.checksum;

            // Useful as a serial
            XmlFsType.VolumeSerial = $"{rootBlk.checksum:X8}";
        }
Пример #20
0
        // TODO: Find root directory on volumes with DiscRecord
        // TODO: Support big directories (ADFS-G?)
        // TODO: Find the real freemap on volumes with DiscRecord, as DiscRecord's discid may be empty but this one isn't
        public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information,
                                   Encoding encoding)
        {
            Encoding = encoding ?? Encoding.GetEncoding("iso-8859-1");
            var sbInformation = new StringBuilder();

            XmlFsType   = new FileSystemType();
            information = "";

            ulong sbSector;

            byte[] sector;
            uint   sectorsToRead;
            ulong  bytes;

            // ADFS-S, ADFS-M, ADFS-L, ADFS-D without partitions
            if (partition.Start == 0)
            {
                sector = imagePlugin.ReadSector(0);
                byte          oldChk0 = AcornMapChecksum(sector, 255);
                OldMapSector0 oldMap0 = Marshal.ByteArrayToStructureLittleEndian <OldMapSector0>(sector);

                sector = imagePlugin.ReadSector(1);
                byte          oldChk1 = AcornMapChecksum(sector, 255);
                OldMapSector1 oldMap1 = Marshal.ByteArrayToStructureLittleEndian <OldMapSector1>(sector);

                // According to documentation map1 MUST start on sector 1. On ADFS-D it starts at 0x100, not on sector 1 (0x400)
                if (oldMap0.checksum == oldChk0 &&
                    oldMap1.checksum != oldChk1 &&
                    sector.Length >= 512)
                {
                    sector = imagePlugin.ReadSector(0);
                    byte[] tmp = new byte[256];
                    Array.Copy(sector, 256, tmp, 0, 256);
                    oldChk1 = AcornMapChecksum(tmp, 255);
                    oldMap1 = Marshal.ByteArrayToStructureLittleEndian <OldMapSector1>(tmp);
                }

                if (oldMap0.checksum == oldChk0 &&
                    oldMap1.checksum == oldChk1 &&
                    oldMap0.checksum != 0 &&
                    oldMap1.checksum != 0)
                {
                    bytes = (ulong)((oldMap0.size[2] << 16) + (oldMap0.size[1] << 8) + oldMap0.size[0]) * 256;
                    byte[] namebytes = new byte[10];

                    for (int i = 0; i < 5; i++)
                    {
                        namebytes[i * 2]       = oldMap0.name[i];
                        namebytes[(i * 2) + 1] = oldMap1.name[i];
                    }

                    XmlFsType = new FileSystemType
                    {
                        Bootable = oldMap1.boot != 0, // Or not?
                        Clusters = bytes / imagePlugin.Info.SectorSize, ClusterSize = imagePlugin.Info.SectorSize,
                        Type     = "Acorn Advanced Disc Filing System"
                    };

                    if (ArrayHelpers.ArrayIsNullOrEmpty(namebytes))
                    {
                        sbSector      = OLD_DIRECTORY_LOCATION / imagePlugin.Info.SectorSize;
                        sectorsToRead = OLD_DIRECTORY_SIZE / imagePlugin.Info.SectorSize;

                        if (OLD_DIRECTORY_SIZE % imagePlugin.Info.SectorSize > 0)
                        {
                            sectorsToRead++;
                        }

                        sector = imagePlugin.ReadSectors(sbSector, sectorsToRead);

                        if (sector.Length > OLD_DIRECTORY_SIZE)
                        {
                            byte[] tmp = new byte[OLD_DIRECTORY_SIZE];
                            Array.Copy(sector, 0, tmp, 0, OLD_DIRECTORY_SIZE - 53);
                            Array.Copy(sector, sector.Length - 54, tmp, OLD_DIRECTORY_SIZE - 54, 53);
                            sector = tmp;
                        }

                        OldDirectory oldRoot = Marshal.ByteArrayToStructureLittleEndian <OldDirectory>(sector);

                        if (oldRoot.header.magic == OLD_DIR_MAGIC &&
                            oldRoot.tail.magic == OLD_DIR_MAGIC)
                        {
                            namebytes = oldRoot.tail.name;
                        }
                        else
                        {
                            // RISC OS says the old directory can't be in the new location, hard disks created by RISC OS 3.10 do that...
                            sbSector      = NEW_DIRECTORY_LOCATION / imagePlugin.Info.SectorSize;
                            sectorsToRead = NEW_DIRECTORY_SIZE / imagePlugin.Info.SectorSize;

                            if (NEW_DIRECTORY_SIZE % imagePlugin.Info.SectorSize > 0)
                            {
                                sectorsToRead++;
                            }

                            sector = imagePlugin.ReadSectors(sbSector, sectorsToRead);

                            if (sector.Length > OLD_DIRECTORY_SIZE)
                            {
                                byte[] tmp = new byte[OLD_DIRECTORY_SIZE];
                                Array.Copy(sector, 0, tmp, 0, OLD_DIRECTORY_SIZE - 53);

                                Array.Copy(sector, sector.Length - 54, tmp, OLD_DIRECTORY_SIZE - 54, 53);

                                sector = tmp;
                            }

                            oldRoot = Marshal.ByteArrayToStructureLittleEndian <OldDirectory>(sector);

                            if (oldRoot.header.magic == OLD_DIR_MAGIC &&
                                oldRoot.tail.magic == OLD_DIR_MAGIC)
                            {
                                namebytes = oldRoot.tail.name;
                            }
                            else
                            {
                                sector = imagePlugin.ReadSectors(sbSector, sectorsToRead);

                                if (sector.Length > NEW_DIRECTORY_SIZE)
                                {
                                    byte[] tmp = new byte[NEW_DIRECTORY_SIZE];
                                    Array.Copy(sector, 0, tmp, 0, NEW_DIRECTORY_SIZE - 41);

                                    Array.Copy(sector, sector.Length - 42, tmp, NEW_DIRECTORY_SIZE - 42, 41);

                                    sector = tmp;
                                }

                                NewDirectory newRoot = Marshal.ByteArrayToStructureLittleEndian <NewDirectory>(sector);

                                if (newRoot.header.magic == NEW_DIR_MAGIC &&
                                    newRoot.tail.magic == NEW_DIR_MAGIC)
                                {
                                    namebytes = newRoot.tail.title;
                                }
                            }
                        }
                    }

                    sbInformation.AppendLine("Acorn Advanced Disc Filing System");
                    sbInformation.AppendLine();
                    sbInformation.AppendFormat("{0} bytes per sector", imagePlugin.Info.SectorSize).AppendLine();
                    sbInformation.AppendFormat("Volume has {0} bytes", bytes).AppendLine();

                    sbInformation.AppendFormat("Volume name: {0}", StringHandlers.CToString(namebytes, Encoding)).
                    AppendLine();

                    if (oldMap1.discId > 0)
                    {
                        XmlFsType.VolumeSerial = $"{oldMap1.discId:X4}";
                        sbInformation.AppendFormat("Volume ID: {0:X4}", oldMap1.discId).AppendLine();
                    }

                    if (!ArrayHelpers.ArrayIsNullOrEmpty(namebytes))
                    {
                        XmlFsType.VolumeName = StringHandlers.CToString(namebytes, Encoding);
                    }

                    information = sbInformation.ToString();

                    return;
                }
            }

            // Partitioning or not, new formats follow:
            DiscRecord drSb;

            sector = imagePlugin.ReadSector(partition.Start);
            byte newChk = NewMapChecksum(sector);

            AaruConsole.DebugWriteLine("ADFS Plugin", "newChk = {0}", newChk);
            AaruConsole.DebugWriteLine("ADFS Plugin", "map.zoneChecksum = {0}", sector[0]);

            sbSector      = BOOT_BLOCK_LOCATION / imagePlugin.Info.SectorSize;
            sectorsToRead = BOOT_BLOCK_SIZE / imagePlugin.Info.SectorSize;

            if (BOOT_BLOCK_SIZE % imagePlugin.Info.SectorSize > 0)
            {
                sectorsToRead++;
            }

            byte[] bootSector = imagePlugin.ReadSectors(sbSector + partition.Start, sectorsToRead);
            int    bootChk    = 0;

            for (int i = 0; i < 0x1FF; i++)
            {
                bootChk = (bootChk & 0xFF) + (bootChk >> 8) + bootSector[i];
            }

            AaruConsole.DebugWriteLine("ADFS Plugin", "bootChk = {0}", bootChk);
            AaruConsole.DebugWriteLine("ADFS Plugin", "bBlock.checksum = {0}", bootSector[0x1FF]);

            if (newChk == sector[0] &&
                newChk != 0)
            {
                NewMap nmap = Marshal.ByteArrayToStructureLittleEndian <NewMap>(sector);
                drSb = nmap.discRecord;
            }
            else if (bootChk == bootSector[0x1FF])
            {
                BootBlock bBlock = Marshal.ByteArrayToStructureLittleEndian <BootBlock>(sector);
                drSb = bBlock.discRecord;
            }
            else
            {
                return;
            }

            AaruConsole.DebugWriteLine("ADFS Plugin", "drSb.log2secsize = {0}", drSb.log2secsize);
            AaruConsole.DebugWriteLine("ADFS Plugin", "drSb.spt = {0}", drSb.spt);
            AaruConsole.DebugWriteLine("ADFS Plugin", "drSb.heads = {0}", drSb.heads);
            AaruConsole.DebugWriteLine("ADFS Plugin", "drSb.density = {0}", drSb.density);
            AaruConsole.DebugWriteLine("ADFS Plugin", "drSb.idlen = {0}", drSb.idlen);
            AaruConsole.DebugWriteLine("ADFS Plugin", "drSb.log2bpmb = {0}", drSb.log2bpmb);
            AaruConsole.DebugWriteLine("ADFS Plugin", "drSb.skew = {0}", drSb.skew);
            AaruConsole.DebugWriteLine("ADFS Plugin", "drSb.bootoption = {0}", drSb.bootoption);
            AaruConsole.DebugWriteLine("ADFS Plugin", "drSb.lowsector = {0}", drSb.lowsector);
            AaruConsole.DebugWriteLine("ADFS Plugin", "drSb.nzones = {0}", drSb.nzones);
            AaruConsole.DebugWriteLine("ADFS Plugin", "drSb.zone_spare = {0}", drSb.zone_spare);
            AaruConsole.DebugWriteLine("ADFS Plugin", "drSb.root = {0}", drSb.root);
            AaruConsole.DebugWriteLine("ADFS Plugin", "drSb.disc_size = {0}", drSb.disc_size);
            AaruConsole.DebugWriteLine("ADFS Plugin", "drSb.disc_id = {0}", drSb.disc_id);

            AaruConsole.DebugWriteLine("ADFS Plugin", "drSb.disc_name = {0}",
                                       StringHandlers.CToString(drSb.disc_name, Encoding));

            AaruConsole.DebugWriteLine("ADFS Plugin", "drSb.disc_type = {0}", drSb.disc_type);
            AaruConsole.DebugWriteLine("ADFS Plugin", "drSb.disc_size_high = {0}", drSb.disc_size_high);
            AaruConsole.DebugWriteLine("ADFS Plugin", "drSb.flags = {0}", drSb.flags);
            AaruConsole.DebugWriteLine("ADFS Plugin", "drSb.nzones_high = {0}", drSb.nzones_high);
            AaruConsole.DebugWriteLine("ADFS Plugin", "drSb.format_version = {0}", drSb.format_version);
            AaruConsole.DebugWriteLine("ADFS Plugin", "drSb.root_size = {0}", drSb.root_size);

            if (drSb.log2secsize < 8 ||
                drSb.log2secsize > 10)
            {
                return;
            }

            if (drSb.idlen < drSb.log2secsize + 3 ||
                drSb.idlen > 19)
            {
                return;
            }

            if (drSb.disc_size_high >> drSb.log2secsize != 0)
            {
                return;
            }

            if (!ArrayHelpers.ArrayIsNullOrEmpty(drSb.reserved))
            {
                return;
            }

            bytes  = drSb.disc_size_high;
            bytes *= 0x100000000;
            bytes += drSb.disc_size;

            ulong zones = drSb.nzones_high;

            zones *= 0x100000000;
            zones += drSb.nzones;

            if (bytes > imagePlugin.Info.Sectors * imagePlugin.Info.SectorSize)
            {
                return;
            }

            XmlFsType = new FileSystemType();

            sbInformation.AppendLine("Acorn Advanced Disc Filing System");
            sbInformation.AppendLine();
            sbInformation.AppendFormat("Version {0}", drSb.format_version).AppendLine();
            sbInformation.AppendFormat("{0} bytes per sector", 1 << drSb.log2secsize).AppendLine();
            sbInformation.AppendFormat("{0} sectors per track", drSb.spt).AppendLine();
            sbInformation.AppendFormat("{0} heads", drSb.heads).AppendLine();
            sbInformation.AppendFormat("Density code: {0}", drSb.density).AppendLine();
            sbInformation.AppendFormat("Skew: {0}", drSb.skew).AppendLine();
            sbInformation.AppendFormat("Boot option: {0}", drSb.bootoption).AppendLine();

            // TODO: What the hell is this field refering to?
            sbInformation.AppendFormat("Root starts at frag {0}", drSb.root).AppendLine();

            //sbInformation.AppendFormat("Root is {0} bytes long", drSb.root_size).AppendLine();
            sbInformation.AppendFormat("Volume has {0} bytes in {1} zones", bytes, zones).AppendLine();
            sbInformation.AppendFormat("Volume flags: 0x{0:X4}", drSb.flags).AppendLine();

            if (drSb.disc_id > 0)
            {
                XmlFsType.VolumeSerial = $"{drSb.disc_id:X4}";
                sbInformation.AppendFormat("Volume ID: {0:X4}", drSb.disc_id).AppendLine();
            }

            if (!ArrayHelpers.ArrayIsNullOrEmpty(drSb.disc_name))
            {
                string discname = StringHandlers.CToString(drSb.disc_name, Encoding);
                XmlFsType.VolumeName = discname;
                sbInformation.AppendFormat("Volume name: {0}", discname).AppendLine();
            }

            information = sbInformation.ToString();

            XmlFsType.Bootable   |= drSb.bootoption != 0;  // Or not?
            XmlFsType.Clusters    = bytes / (ulong)(1 << drSb.log2secsize);
            XmlFsType.ClusterSize = (uint)(1 << drSb.log2secsize);
            XmlFsType.Type        = "Acorn Advanced Disc Filing System";
        }
Пример #21
0
        public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information,
                                   Encoding encoding)
        {
            Encoding    = encoding ?? Encoding.GetEncoding("iso-8859-15");
            information = "";
            if (imagePlugin.Info.SectorSize < 512)
            {
                return;
            }

            EFS_Superblock efsSb = new EFS_Superblock();

            // Misaligned
            if (imagePlugin.Info.XmlMediaType == XmlMediaType.OpticalDisc)
            {
                uint sbSize = (uint)((Marshal.SizeOf <EFS_Superblock>() + 0x400) / imagePlugin.Info.SectorSize);
                if ((Marshal.SizeOf <EFS_Superblock>() + 0x400) % imagePlugin.Info.SectorSize != 0)
                {
                    sbSize++;
                }

                byte[] sector = imagePlugin.ReadSectors(partition.Start, sbSize);
                if (sector.Length < Marshal.SizeOf <EFS_Superblock>())
                {
                    return;
                }

                byte[] sbpiece = new byte[Marshal.SizeOf <EFS_Superblock>()];

                Array.Copy(sector, 0x200, sbpiece, 0, Marshal.SizeOf <EFS_Superblock>());

                efsSb = Marshal.ByteArrayToStructureBigEndian <EFS_Superblock>(sbpiece);

                DicConsole.DebugWriteLine("EFS plugin", "magic at 0x{0:X3} = 0x{1:X8} (expected 0x{2:X8} or 0x{3:X8})",
                                          0x200, efsSb.sb_magic, EFS_MAGIC, EFS_MAGIC_NEW);
            }
            else
            {
                uint sbSize = (uint)(Marshal.SizeOf <EFS_Superblock>() / imagePlugin.Info.SectorSize);
                if (Marshal.SizeOf <EFS_Superblock>() % imagePlugin.Info.SectorSize != 0)
                {
                    sbSize++;
                }

                byte[] sector = imagePlugin.ReadSectors(partition.Start + 1, sbSize);
                if (sector.Length < Marshal.SizeOf <EFS_Superblock>())
                {
                    return;
                }

                efsSb = Marshal.ByteArrayToStructureBigEndian <EFS_Superblock>(sector);

                DicConsole.DebugWriteLine("EFS plugin", "magic at {0} = 0x{1:X8} (expected 0x{2:X8} or 0x{3:X8})", 1,
                                          efsSb.sb_magic, EFS_MAGIC, EFS_MAGIC_NEW);
            }

            if (efsSb.sb_magic != EFS_MAGIC && efsSb.sb_magic != EFS_MAGIC_NEW)
            {
                return;
            }

            StringBuilder sb = new StringBuilder();

            sb.AppendLine("SGI extent filesystem");
            if (efsSb.sb_magic == EFS_MAGIC_NEW)
            {
                sb.AppendLine("New version");
            }
            sb.AppendFormat("Filesystem size: {0} basic blocks", efsSb.sb_size).AppendLine();
            sb.AppendFormat("First cylinder group starts at block {0}", efsSb.sb_firstcg).AppendLine();
            sb.AppendFormat("Cylinder group size: {0} basic blocks", efsSb.sb_cgfsize).AppendLine();
            sb.AppendFormat("{0} inodes per cylinder group", efsSb.sb_cgisize).AppendLine();
            sb.AppendFormat("{0} sectors per track", efsSb.sb_sectors).AppendLine();
            sb.AppendFormat("{0} heads per cylinder", efsSb.sb_heads).AppendLine();
            sb.AppendFormat("{0} cylinder groups", efsSb.sb_ncg).AppendLine();
            sb.AppendFormat("Volume created on {0}", DateHandlers.UnixToDateTime(efsSb.sb_time)).AppendLine();
            sb.AppendFormat("{0} bytes on bitmap", efsSb.sb_bmsize).AppendLine();
            sb.AppendFormat("{0} free blocks", efsSb.sb_tfree).AppendLine();
            sb.AppendFormat("{0} free inodes", efsSb.sb_tinode).AppendLine();
            if (efsSb.sb_bmblock > 0)
            {
                sb.AppendFormat("Bitmap resides at block {0}", efsSb.sb_bmblock).AppendLine();
            }
            if (efsSb.sb_replsb > 0)
            {
                sb.AppendFormat("Replacement superblock resides at block {0}", efsSb.sb_replsb).AppendLine();
            }
            if (efsSb.sb_lastinode > 0)
            {
                sb.AppendFormat("Last inode allocated: {0}", efsSb.sb_lastinode).AppendLine();
            }
            if (efsSb.sb_dirty > 0)
            {
                sb.AppendLine("Volume is dirty");
            }
            sb.AppendFormat("Checksum: 0x{0:X8}", efsSb.sb_checksum).AppendLine();
            sb.AppendFormat("Volume name: {0}", StringHandlers.CToString(efsSb.sb_fname, Encoding)).AppendLine();
            sb.AppendFormat("Volume pack: {0}", StringHandlers.CToString(efsSb.sb_fpack, Encoding)).AppendLine();

            information = sb.ToString();

            XmlFsType = new FileSystemType
            {
                Type                  = "Extent File System",
                ClusterSize           = 512,
                Clusters              = (ulong)efsSb.sb_size,
                FreeClusters          = (ulong)efsSb.sb_tfree,
                FreeClustersSpecified = true,
                Dirty                 = efsSb.sb_dirty > 0,
                VolumeName            = StringHandlers.CToString(efsSb.sb_fname, Encoding),
                VolumeSerial          = $"{efsSb.sb_checksum:X8}",
                CreationDate          = DateHandlers.UnixToDateTime(efsSb.sb_time),
                CreationDateSpecified = true
            };
        }
Пример #22
0
        // TODO: BBC Master hard disks are untested...
        public bool Identify(IMediaImage imagePlugin, Partition partition)
        {
            if (partition.Start >= partition.End)
            {
                return(false);
            }

            ulong sbSector;
            uint  sectorsToRead;

            if (imagePlugin.Info.SectorSize < 256)
            {
                return(false);
            }

            byte[] sector;

            // ADFS-S, ADFS-M, ADFS-L, ADFS-D without partitions
            if (partition.Start == 0)
            {
                sector = imagePlugin.ReadSector(0);
                byte          oldChk0 = AcornMapChecksum(sector, 255);
                OldMapSector0 oldMap0 = Marshal.ByteArrayToStructureLittleEndian <OldMapSector0>(sector);

                sector = imagePlugin.ReadSector(1);
                byte          oldChk1 = AcornMapChecksum(sector, 255);
                OldMapSector1 oldMap1 = Marshal.ByteArrayToStructureLittleEndian <OldMapSector1>(sector);

                AaruConsole.DebugWriteLine("ADFS Plugin", "oldMap0.checksum = {0}", oldMap0.checksum);
                AaruConsole.DebugWriteLine("ADFS Plugin", "oldChk0 = {0}", oldChk0);

                // According to documentation map1 MUST start on sector 1. On ADFS-D it starts at 0x100, not on sector 1 (0x400)
                if (oldMap0.checksum == oldChk0 &&
                    oldMap1.checksum != oldChk1 &&
                    sector.Length >= 512)
                {
                    sector = imagePlugin.ReadSector(0);
                    byte[] tmp = new byte[256];
                    Array.Copy(sector, 256, tmp, 0, 256);
                    oldChk1 = AcornMapChecksum(tmp, 255);
                    oldMap1 = Marshal.ByteArrayToStructureLittleEndian <OldMapSector1>(tmp);
                }

                AaruConsole.DebugWriteLine("ADFS Plugin", "oldMap1.checksum = {0}", oldMap1.checksum);
                AaruConsole.DebugWriteLine("ADFS Plugin", "oldChk1 = {0}", oldChk1);

                if (oldMap0.checksum == oldChk0 &&
                    oldMap1.checksum == oldChk1 &&
                    oldMap0.checksum != 0 &&
                    oldMap1.checksum != 0)
                {
                    sbSector      = OLD_DIRECTORY_LOCATION / imagePlugin.Info.SectorSize;
                    sectorsToRead = OLD_DIRECTORY_SIZE / imagePlugin.Info.SectorSize;

                    if (OLD_DIRECTORY_SIZE % imagePlugin.Info.SectorSize > 0)
                    {
                        sectorsToRead++;
                    }

                    sector = imagePlugin.ReadSectors(sbSector, sectorsToRead);

                    if (sector.Length > OLD_DIRECTORY_SIZE)
                    {
                        byte[] tmp = new byte[OLD_DIRECTORY_SIZE];
                        Array.Copy(sector, 0, tmp, 0, OLD_DIRECTORY_SIZE - 53);
                        Array.Copy(sector, sector.Length - 54, tmp, OLD_DIRECTORY_SIZE - 54, 53);
                        sector = tmp;
                    }

                    OldDirectory oldRoot = Marshal.ByteArrayToStructureLittleEndian <OldDirectory>(sector);
                    byte         dirChk  = AcornDirectoryChecksum(sector, (int)OLD_DIRECTORY_SIZE - 1);

                    AaruConsole.DebugWriteLine("ADFS Plugin", "oldRoot.header.magic at 0x200 = {0}",
                                               oldRoot.header.magic);

                    AaruConsole.DebugWriteLine("ADFS Plugin", "oldRoot.tail.magic at 0x200 = {0}", oldRoot.tail.magic);

                    AaruConsole.DebugWriteLine("ADFS Plugin", "oldRoot.tail.checkByte at 0x200 = {0}",
                                               oldRoot.tail.checkByte);

                    AaruConsole.DebugWriteLine("ADFS Plugin", "dirChk at 0x200 = {0}", dirChk);

                    if ((oldRoot.header.magic == OLD_DIR_MAGIC && oldRoot.tail.magic == OLD_DIR_MAGIC) ||
                        (oldRoot.header.magic == NEW_DIR_MAGIC && oldRoot.tail.magic == NEW_DIR_MAGIC))
                    {
                        return(true);
                    }

                    // RISC OS says the old directory can't be in the new location, hard disks created by RISC OS 3.10 do that...
                    sbSector      = NEW_DIRECTORY_LOCATION / imagePlugin.Info.SectorSize;
                    sectorsToRead = NEW_DIRECTORY_SIZE / imagePlugin.Info.SectorSize;

                    if (NEW_DIRECTORY_SIZE % imagePlugin.Info.SectorSize > 0)
                    {
                        sectorsToRead++;
                    }

                    sector = imagePlugin.ReadSectors(sbSector, sectorsToRead);

                    if (sector.Length > OLD_DIRECTORY_SIZE)
                    {
                        byte[] tmp = new byte[OLD_DIRECTORY_SIZE];
                        Array.Copy(sector, 0, tmp, 0, OLD_DIRECTORY_SIZE - 53);
                        Array.Copy(sector, sector.Length - 54, tmp, OLD_DIRECTORY_SIZE - 54, 53);
                        sector = tmp;
                    }

                    oldRoot = Marshal.ByteArrayToStructureLittleEndian <OldDirectory>(sector);
                    dirChk  = AcornDirectoryChecksum(sector, (int)OLD_DIRECTORY_SIZE - 1);

                    AaruConsole.DebugWriteLine("ADFS Plugin", "oldRoot.header.magic at 0x400 = {0}",
                                               oldRoot.header.magic);

                    AaruConsole.DebugWriteLine("ADFS Plugin", "oldRoot.tail.magic at 0x400 = {0}", oldRoot.tail.magic);

                    AaruConsole.DebugWriteLine("ADFS Plugin", "oldRoot.tail.checkByte at 0x400 = {0}",
                                               oldRoot.tail.checkByte);

                    AaruConsole.DebugWriteLine("ADFS Plugin", "dirChk at 0x400 = {0}", dirChk);

                    if ((oldRoot.header.magic == OLD_DIR_MAGIC && oldRoot.tail.magic == OLD_DIR_MAGIC) ||
                        (oldRoot.header.magic == NEW_DIR_MAGIC && oldRoot.tail.magic == NEW_DIR_MAGIC))
                    {
                        return(true);
                    }
                }
            }

            // Partitioning or not, new formats follow:
            DiscRecord drSb;

            sector = imagePlugin.ReadSector(partition.Start);
            byte newChk = NewMapChecksum(sector);

            AaruConsole.DebugWriteLine("ADFS Plugin", "newChk = {0}", newChk);
            AaruConsole.DebugWriteLine("ADFS Plugin", "map.zoneChecksum = {0}", sector[0]);

            sbSector      = BOOT_BLOCK_LOCATION / imagePlugin.Info.SectorSize;
            sectorsToRead = BOOT_BLOCK_SIZE / imagePlugin.Info.SectorSize;

            if (BOOT_BLOCK_SIZE % imagePlugin.Info.SectorSize > 0)
            {
                sectorsToRead++;
            }

            if (sbSector + partition.Start + sectorsToRead >= partition.End)
            {
                return(false);
            }

            byte[] bootSector = imagePlugin.ReadSectors(sbSector + partition.Start, sectorsToRead);
            int    bootChk    = 0;

            if (bootSector.Length < 512)
            {
                return(false);
            }

            for (int i = 0; i < 0x1FF; i++)
            {
                bootChk = (bootChk & 0xFF) + (bootChk >> 8) + bootSector[i];
            }

            AaruConsole.DebugWriteLine("ADFS Plugin", "bootChk = {0}", bootChk);
            AaruConsole.DebugWriteLine("ADFS Plugin", "bBlock.checksum = {0}", bootSector[0x1FF]);

            if (newChk == sector[0] &&
                newChk != 0)
            {
                NewMap nmap = Marshal.ByteArrayToStructureLittleEndian <NewMap>(sector);
                drSb = nmap.discRecord;
            }
            else if (bootChk == bootSector[0x1FF])
            {
                BootBlock bBlock = Marshal.ByteArrayToStructureLittleEndian <BootBlock>(bootSector);
                drSb = bBlock.discRecord;
            }
            else
            {
                return(false);
            }

            AaruConsole.DebugWriteLine("ADFS Plugin", "drSb.log2secsize = {0}", drSb.log2secsize);
            AaruConsole.DebugWriteLine("ADFS Plugin", "drSb.idlen = {0}", drSb.idlen);
            AaruConsole.DebugWriteLine("ADFS Plugin", "drSb.disc_size_high = {0}", drSb.disc_size_high);
            AaruConsole.DebugWriteLine("ADFS Plugin", "drSb.disc_size = {0}", drSb.disc_size);

            AaruConsole.DebugWriteLine("ADFS Plugin", "IsNullOrEmpty(drSb.reserved) = {0}",
                                       ArrayHelpers.ArrayIsNullOrEmpty(drSb.reserved));

            if (drSb.log2secsize < 8 ||
                drSb.log2secsize > 10)
            {
                return(false);
            }

            if (drSb.idlen < drSb.log2secsize + 3 ||
                drSb.idlen > 19)
            {
                return(false);
            }

            if (drSb.disc_size_high >> drSb.log2secsize != 0)
            {
                return(false);
            }

            if (!ArrayHelpers.ArrayIsNullOrEmpty(drSb.reserved))
            {
                return(false);
            }

            ulong bytes = drSb.disc_size_high;

            bytes *= 0x100000000;
            bytes += drSb.disc_size;

            return(bytes <= imagePlugin.Info.Sectors * imagePlugin.Info.SectorSize);
        }
        public void Convert()
        {
            Environment.CurrentDirectory = DataFolder;

            Resume           resume  = null;
            CICMMetadataType sidecar = null;

            var     filtersList = new FiltersList();
            IFilter inputFilter = filtersList.GetFilter(InputPath);

            Assert.IsNotNull(inputFilter, "Cannot open specified file.");

            string outputPath = Path.Combine(Path.GetTempPath(), SuggestedOutputFilename);

            Assert.IsFalse(File.Exists(outputPath), "Output file already exists, not continuing.");

            IMediaImage inputFormat = ImageFormat.Detect(inputFilter);

            Assert.IsNotNull(inputFormat, "Input image format not identified, not proceeding with conversion.");

            Assert.IsTrue(inputFormat.Open(inputFilter), "Unable to open image format");

            Assert.IsTrue(OutputFormat.SupportedMediaTypes.Contains(inputFormat.Info.MediaType),
                          "Output format does not support media type, cannot continue...");

            if (inputFormat.Info.ReadableSectorTags.Count == 0)
            {
                Assert.IsFalse(UseLong, "Input image does not support long sectors.");
            }

            var inputOptical  = inputFormat as IOpticalMediaImage;
            var outputOptical = OutputFormat as IWritableOpticalImage;

            Assert.IsNotNull(inputOptical, "Could not treat existing image as optical disc.");
            Assert.IsNotNull(outputOptical, "Could not treat new image as optical disc.");
            Assert.IsNotNull(inputOptical.Tracks, "Existing image contains no tracks.");

            Assert.IsTrue(outputOptical.Create(outputPath, inputFormat.Info.MediaType, ParsedOptions, inputFormat.Info.Sectors, inputFormat.Info.SectorSize),
                          $"Error {outputOptical.ErrorMessage} creating output image.");

            var metadata = new ImageInfo
            {
                Application           = "Aaru",
                ApplicationVersion    = Version.GetVersion(),
                Comments              = inputFormat.Info.Comments,
                Creator               = inputFormat.Info.Creator,
                DriveFirmwareRevision = inputFormat.Info.DriveFirmwareRevision,
                DriveManufacturer     = inputFormat.Info.DriveManufacturer,
                DriveModel            = inputFormat.Info.DriveModel,
                DriveSerialNumber     = inputFormat.Info.DriveSerialNumber,
                LastMediaSequence     = inputFormat.Info.LastMediaSequence,
                MediaBarcode          = inputFormat.Info.MediaBarcode,
                MediaManufacturer     = inputFormat.Info.MediaManufacturer,
                MediaModel            = inputFormat.Info.MediaModel,
                MediaPartNumber       = inputFormat.Info.MediaPartNumber,
                MediaSequence         = inputFormat.Info.MediaSequence,
                MediaSerialNumber     = inputFormat.Info.MediaSerialNumber,
                MediaTitle            = inputFormat.Info.MediaTitle
            };

            Assert.IsTrue(outputOptical.SetMetadata(metadata),
                          $"Error {outputOptical.ErrorMessage} setting metadata, ");

            CICMMetadataType        cicmMetadata = inputFormat.CicmMetadata;
            List <DumpHardwareType> dumpHardware = inputFormat.DumpHardware;

            foreach (MediaTagType mediaTag in inputFormat.Info.ReadableMediaTags.Where(mediaTag =>
                                                                                       outputOptical.SupportedMediaTags.Contains(mediaTag)))
            {
                AaruConsole.WriteLine("Converting media tag {0}", mediaTag);
                byte[] tag = inputFormat.ReadDiskTag(mediaTag);

                Assert.IsTrue(outputOptical.WriteMediaTag(tag, mediaTag));
            }

            AaruConsole.WriteLine("{0} sectors to convert", inputFormat.Info.Sectors);
            ulong doneSectors;

            Assert.IsTrue(outputOptical.SetTracks(inputOptical.Tracks),
                          $"Error {outputOptical.ErrorMessage} sending tracks list to output image.");

            foreach (Track track in inputOptical.Tracks)
            {
                doneSectors = 0;
                ulong trackSectors = track.TrackEndSector - track.TrackStartSector + 1;

                while (doneSectors < trackSectors)
                {
                    byte[] sector;

                    uint sectorsToDo;

                    if (trackSectors - doneSectors >= SECTORS_TO_READ)
                    {
                        sectorsToDo = SECTORS_TO_READ;
                    }
                    else
                    {
                        sectorsToDo = (uint)(trackSectors - doneSectors);
                    }

                    bool useNotLong = false;
                    bool result     = false;

                    if (UseLong)
                    {
                        if (sectorsToDo == 1)
                        {
                            sector = inputFormat.ReadSectorLong(doneSectors + track.TrackStartSector);
                            result = outputOptical.WriteSectorLong(sector, doneSectors + track.TrackStartSector);
                        }
                        else
                        {
                            sector = inputFormat.ReadSectorsLong(doneSectors + track.TrackStartSector, sectorsToDo);

                            result = outputOptical.WriteSectorsLong(sector, doneSectors + track.TrackStartSector,
                                                                    sectorsToDo);
                        }

                        if (!result &&
                            sector.Length % 2352 != 0)
                        {
                            useNotLong = true;
                        }
                    }

                    if (!UseLong || useNotLong)
                    {
                        if (sectorsToDo == 1)
                        {
                            sector = inputFormat.ReadSector(doneSectors + track.TrackStartSector);
                            result = outputOptical.WriteSector(sector, doneSectors + track.TrackStartSector);
                        }
                        else
                        {
                            sector = inputFormat.ReadSectors(doneSectors + track.TrackStartSector, sectorsToDo);

                            result = outputOptical.WriteSectors(sector, doneSectors + track.TrackStartSector,
                                                                sectorsToDo);
                        }
                    }

                    Assert.IsTrue(result,
                                  $"Error {outputOptical.ErrorMessage} writing sector {doneSectors + track.TrackStartSector}, not continuing...");

                    doneSectors += sectorsToDo;
                }
            }

            Dictionary <byte, string> isrcs                  = new Dictionary <byte, string>();
            Dictionary <byte, byte>   trackFlags             = new Dictionary <byte, byte>();
            string                 mcn                       = null;
            HashSet <int>          subchannelExtents         = new HashSet <int>();
            Dictionary <byte, int> smallestPregapLbaPerTrack = new Dictionary <byte, int>();

            Track[] tracks = new Track[inputOptical.Tracks.Count];

            for (int i = 0; i < tracks.Length; i++)
            {
                tracks[i] = new Track
                {
                    Indexes                = new Dictionary <ushort, int>(),
                    TrackDescription       = inputOptical.Tracks[i].TrackDescription,
                    TrackEndSector         = inputOptical.Tracks[i].TrackEndSector,
                    TrackStartSector       = inputOptical.Tracks[i].TrackStartSector,
                    TrackPregap            = inputOptical.Tracks[i].TrackPregap,
                    TrackSequence          = inputOptical.Tracks[i].TrackSequence,
                    TrackSession           = inputOptical.Tracks[i].TrackSession,
                    TrackBytesPerSector    = inputOptical.Tracks[i].TrackBytesPerSector,
                    TrackRawBytesPerSector = inputOptical.Tracks[i].TrackRawBytesPerSector,
                    TrackType              = inputOptical.Tracks[i].TrackType,
                    TrackSubchannelType    = inputOptical.Tracks[i].TrackSubchannelType
                };

                foreach (KeyValuePair <ushort, int> idx in inputOptical.Tracks[i].Indexes)
                {
                    tracks[i].Indexes[idx.Key] = idx.Value;
                }
            }

            foreach (SectorTagType tag in inputFormat.Info.ReadableSectorTags.Where(t => t == SectorTagType.CdTrackIsrc).
                     OrderBy(t => t))
            {
                foreach (Track track in tracks)
                {
                    byte[] isrc = inputFormat.ReadSectorTag(track.TrackSequence, tag);

                    if (isrc is null)
                    {
                        continue;
                    }

                    isrcs[(byte)track.TrackSequence] = Encoding.UTF8.GetString(isrc);
                }
            }

            foreach (SectorTagType tag in inputFormat.Info.ReadableSectorTags.
                     Where(t => t == SectorTagType.CdTrackFlags).OrderBy(t => t))
            {
                foreach (Track track in tracks)
                {
                    byte[] flags = inputFormat.ReadSectorTag(track.TrackSequence, tag);

                    if (flags is null)
                    {
                        continue;
                    }

                    trackFlags[(byte)track.TrackSequence] = flags[0];
                }
            }

            for (ulong s = 0; s < inputFormat.Info.Sectors; s++)
            {
                if (s > int.MaxValue)
                {
                    break;
                }

                subchannelExtents.Add((int)s);
            }

            foreach (SectorTagType tag in inputFormat.Info.ReadableSectorTags.OrderBy(t => t).TakeWhile(tag => UseLong))
            {
                switch (tag)
                {
                case SectorTagType.AppleSectorTag:
                case SectorTagType.CdSectorSync:
                case SectorTagType.CdSectorHeader:
                case SectorTagType.CdSectorSubHeader:
                case SectorTagType.CdSectorEdc:
                case SectorTagType.CdSectorEccP:
                case SectorTagType.CdSectorEccQ:
                case SectorTagType.CdSectorEcc:
                    // This tags are inline in long sector
                    continue;
                }

                if (!outputOptical.SupportedSectorTags.Contains(tag))
                {
                    continue;
                }

                foreach (Track track in inputOptical.Tracks)
                {
                    doneSectors = 0;
                    ulong  trackSectors = track.TrackEndSector - track.TrackStartSector + 1;
                    byte[] sector;
                    bool   result;

                    switch (tag)
                    {
                    case SectorTagType.CdTrackFlags:
                    case SectorTagType.CdTrackIsrc:
                        sector = inputFormat.ReadSectorTag(track.TrackSequence, tag);
                        result = outputOptical.WriteSectorTag(sector, track.TrackSequence, tag);

                        Assert.IsTrue(result, $"Error {outputOptical.ErrorMessage} writing tag, not continuing...");

                        continue;
                    }

                    while (doneSectors < trackSectors)
                    {
                        uint sectorsToDo;

                        if (trackSectors - doneSectors >= SECTORS_TO_READ)
                        {
                            sectorsToDo = SECTORS_TO_READ;
                        }
                        else
                        {
                            sectorsToDo = (uint)(trackSectors - doneSectors);
                        }

                        if (sectorsToDo == 1)
                        {
                            sector = inputFormat.ReadSectorTag(doneSectors + track.TrackStartSector, tag);

                            if (tag == SectorTagType.CdSectorSubchannel)
                            {
                                bool indexesChanged = CompactDisc.WriteSubchannelToImage(MmcSubchannel.Raw,
                                                                                         MmcSubchannel.Raw, sector, doneSectors + track.TrackStartSector, 1, null,
                                                                                         isrcs, (byte)track.TrackSequence, ref mcn, tracks, subchannelExtents, true,
                                                                                         outputOptical, true, true, null, null, smallestPregapLbaPerTrack, false);

                                if (indexesChanged)
                                {
                                    outputOptical.SetTracks(tracks.ToList());
                                }

                                result = true;
                            }
                            else
                            {
                                result = outputOptical.WriteSectorTag(sector, doneSectors + track.TrackStartSector,
                                                                      tag);
                            }
                        }
                        else
                        {
                            sector = inputFormat.ReadSectorsTag(doneSectors + track.TrackStartSector, sectorsToDo, tag);

                            if (tag == SectorTagType.CdSectorSubchannel)
                            {
                                bool indexesChanged = CompactDisc.WriteSubchannelToImage(MmcSubchannel.Raw,
                                                                                         MmcSubchannel.Raw, sector, doneSectors + track.TrackStartSector, sectorsToDo,
                                                                                         null, isrcs, (byte)track.TrackSequence, ref mcn, tracks, subchannelExtents,
                                                                                         true, outputOptical, true, true, null, null, smallestPregapLbaPerTrack, false);

                                if (indexesChanged)
                                {
                                    outputOptical.SetTracks(tracks.ToList());
                                }

                                result = true;
                            }
                            else
                            {
                                result = outputOptical.WriteSectorsTag(sector, doneSectors + track.TrackStartSector,
                                                                       sectorsToDo, tag);
                            }
                        }

                        Assert.IsTrue(result,
                                      $"Error {outputOptical.ErrorMessage} writing tag for sector {doneSectors + track.TrackStartSector}, not continuing...");

                        doneSectors += sectorsToDo;
                    }
                }
            }

            if (isrcs.Count > 0)
            {
                foreach (KeyValuePair <byte, string> isrc in isrcs)
                {
                    outputOptical.WriteSectorTag(Encoding.UTF8.GetBytes(isrc.Value), isrc.Key,
                                                 SectorTagType.CdTrackIsrc);
                }
            }

            if (trackFlags.Count > 0)
            {
                foreach ((byte track, byte flags) in trackFlags)
                {
                    outputOptical.WriteSectorTag(new[]
                    {
                        flags
                    }, track, SectorTagType.CdTrackFlags);
                }
            }

            if (mcn != null)
            {
                outputOptical.WriteMediaTag(Encoding.UTF8.GetBytes(mcn), MediaTagType.CD_MCN);
            }

            if (resume != null ||
                dumpHardware != null)
            {
                if (resume != null)
                {
                    outputOptical.SetDumpHardware(resume.Tries);
                }
                else if (dumpHardware != null)
                {
                    outputOptical.SetDumpHardware(dumpHardware);
                }
            }

            if (sidecar != null ||
                cicmMetadata != null)
            {
                if (sidecar != null)
                {
                    outputOptical.SetCicmMetadata(sidecar);
                }
                else if (cicmMetadata != null)
                {
                    outputOptical.SetCicmMetadata(cicmMetadata);
                }
            }

            Assert.True(outputOptical.Close(),
                        $"Error {outputOptical.ErrorMessage} closing output image... Contents are not correct.");

            // Some images will never generate the same
            if (Md5 != null)
            {
                string md5 = Md5Context.File(outputPath, out _);

                Assert.AreEqual(Md5, md5, "Hashes are different");
            }

            File.Delete(outputPath);
        }
Пример #24
0
        public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information,
                                   Encoding encoding)
        {
            Encoding    = Encoding.UTF8;
            information = "";

            uint sbSize = (uint)(Marshal.SizeOf <RefsVolumeHeader>() / imagePlugin.Info.SectorSize);

            if (Marshal.SizeOf <RefsVolumeHeader>() % imagePlugin.Info.SectorSize != 0)
            {
                sbSize++;
            }

            if (partition.Start + sbSize >= partition.End)
            {
                return;
            }

            byte[] sector = imagePlugin.ReadSectors(partition.Start, sbSize);
            if (sector.Length < Marshal.SizeOf <RefsVolumeHeader>())
            {
                return;
            }

            RefsVolumeHeader refsVhdr = Marshal.ByteArrayToStructureLittleEndian <RefsVolumeHeader>(sector);

            DicConsole.DebugWriteLine("ReFS plugin", "VolumeHeader.jump empty? = {0}",
                                      ArrayHelpers.ArrayIsNullOrEmpty(refsVhdr.jump));
            DicConsole.DebugWriteLine("ReFS plugin", "VolumeHeader.signature = {0}",
                                      StringHandlers.CToString(refsVhdr.signature));
            DicConsole.DebugWriteLine("ReFS plugin", "VolumeHeader.mustBeZero empty? = {0}",
                                      ArrayHelpers.ArrayIsNullOrEmpty(refsVhdr.mustBeZero));
            DicConsole.DebugWriteLine("ReFS plugin", "VolumeHeader.identifier = {0}",
                                      StringHandlers.CToString(BitConverter.GetBytes(refsVhdr.identifier)));
            DicConsole.DebugWriteLine("ReFS plugin", "VolumeHeader.length = {0}", refsVhdr.length);
            DicConsole.DebugWriteLine("ReFS plugin", "VolumeHeader.checksum = 0x{0:X4}", refsVhdr.checksum);
            DicConsole.DebugWriteLine("ReFS plugin", "VolumeHeader.sectors = {0}", refsVhdr.sectors);
            DicConsole.DebugWriteLine("ReFS plugin", "VolumeHeader.bytesPerSector = {0}", refsVhdr.bytesPerSector);
            DicConsole.DebugWriteLine("ReFS plugin", "VolumeHeader.sectorsPerCluster = {0}",
                                      refsVhdr.sectorsPerCluster);
            DicConsole.DebugWriteLine("ReFS plugin", "VolumeHeader.unknown1 zero? = {0}", refsVhdr.unknown1 == 0);
            DicConsole.DebugWriteLine("ReFS plugin", "VolumeHeader.unknown2 zero? = {0}", refsVhdr.unknown2 == 0);
            DicConsole.DebugWriteLine("ReFS plugin", "VolumeHeader.unknown3 zero? = {0}", refsVhdr.unknown3 == 0);
            DicConsole.DebugWriteLine("ReFS plugin", "VolumeHeader.unknown4 zero? = {0}", refsVhdr.unknown4 == 0);
            DicConsole.DebugWriteLine("ReFS plugin", "VolumeHeader.unknown5 empty? = {0}",
                                      ArrayHelpers.ArrayIsNullOrEmpty(refsVhdr.unknown5));

            if (refsVhdr.identifier != FSRS || !ArrayHelpers.ArrayIsNullOrEmpty(refsVhdr.mustBeZero) ||
                !refsVhdr.signature.SequenceEqual(refsSignature))
            {
                return;
            }

            StringBuilder sb = new StringBuilder();

            sb.AppendLine("Microsoft Resilient File System");
            sb.AppendFormat("Volume uses {0} bytes per sector", refsVhdr.bytesPerSector).AppendLine();
            sb.AppendFormat("Volume uses {0} sectors per cluster ({1} bytes)", refsVhdr.sectorsPerCluster,
                            refsVhdr.sectorsPerCluster * refsVhdr.bytesPerSector).AppendLine();
            sb.AppendFormat("Volume has {0} sectors ({1} bytes)", refsVhdr.sectors,
                            refsVhdr.sectors * refsVhdr.bytesPerSector).AppendLine();

            information = sb.ToString();

            XmlFsType = new FileSystemType
            {
                Type        = "Resilient File System",
                ClusterSize = refsVhdr.bytesPerSector * refsVhdr.sectorsPerCluster,
                Clusters    = refsVhdr.sectors / refsVhdr.sectorsPerCluster
            };
        }
Пример #25
0
        public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information,
                                   Encoding encoding)
        {
            Encoding = encoding ?? Encoding.GetEncoding("shift_jis");
            var sbInformation = new StringBuilder();

            information = "";
            XmlFsType   = new FileSystemType();

            var fields = new NintendoFields();

            byte[] header = imagePlugin.ReadSectors(0, 0x50000 / imagePlugin.Info.SectorSize);

            bool wii = false;

            uint magicGc  = BigEndianBitConverter.ToUInt32(header, 0x1C);
            uint magicWii = BigEndianBitConverter.ToUInt32(header, 0x18);

            if (magicWii == 0x5D1C9EA3)
            {
                wii = true;
            }
            else if (magicGc != 0xC2339F3D)
            {
                return;
            }

            fields.DiscType         = Encoding.ASCII.GetString(header, 0, 1);
            fields.GameCode         = Encoding.ASCII.GetString(header, 1, 2);
            fields.RegionCode       = Encoding.ASCII.GetString(header, 3, 1);
            fields.PublisherCode    = Encoding.ASCII.GetString(header, 4, 2);
            fields.DiscId           = Encoding.ASCII.GetString(header, 0, 6);
            fields.DiscNumber       = header[6];
            fields.DiscVersion      = header[7];
            fields.Streaming       |= header[8] > 0;
            fields.StreamBufferSize = header[9];
            byte[] temp = new byte[64];
            Array.Copy(header, 0x20, temp, 0, 64);
            fields.Title = StringHandlers.CToString(temp, Encoding);

            if (!wii)
            {
                fields.DebugOff  = BigEndianBitConverter.ToUInt32(header, 0x0400);
                fields.DebugAddr = BigEndianBitConverter.ToUInt32(header, 0x0404);
                fields.DolOff    = BigEndianBitConverter.ToUInt32(header, 0x0420);
                fields.FstOff    = BigEndianBitConverter.ToUInt32(header, 0x0424);
                fields.FstSize   = BigEndianBitConverter.ToUInt32(header, 0x0428);
                fields.FstMax    = BigEndianBitConverter.ToUInt32(header, 0x042C);
            }

            if (wii)
            {
                uint offset1 = BigEndianBitConverter.ToUInt32(header, 0x40004) << 2;
                uint offset2 = BigEndianBitConverter.ToUInt32(header, 0x4000C) << 2;
                uint offset3 = BigEndianBitConverter.ToUInt32(header, 0x40014) << 2;
                uint offset4 = BigEndianBitConverter.ToUInt32(header, 0x4001C) << 2;

                fields.FirstPartitions  = new NintendoPartition[BigEndianBitConverter.ToUInt32(header, 0x40000)];
                fields.SecondPartitions = new NintendoPartition[BigEndianBitConverter.ToUInt32(header, 0x40008)];
                fields.ThirdPartitions  = new NintendoPartition[BigEndianBitConverter.ToUInt32(header, 0x40010)];
                fields.FourthPartitions = new NintendoPartition[BigEndianBitConverter.ToUInt32(header, 0x40018)];

                for (int i = 0; i < fields.FirstPartitions.Length; i++)
                {
                    if (offset1 + (i * 8) + 8 < 0x50000)
                    {
                        fields.FirstPartitions[i].Offset =
                            BigEndianBitConverter.ToUInt32(header, (int)(offset1 + (i * 8) + 0)) << 2;

                        fields.FirstPartitions[i].Type =
                            BigEndianBitConverter.ToUInt32(header, (int)(offset1 + (i * 8) + 4));
                    }
                }

                for (int i = 0; i < fields.SecondPartitions.Length; i++)
                {
                    if (offset1 + (i * 8) + 8 < 0x50000)
                    {
                        fields.FirstPartitions[i].Offset =
                            BigEndianBitConverter.ToUInt32(header, (int)(offset2 + (i * 8) + 0)) << 2;

                        fields.FirstPartitions[i].Type =
                            BigEndianBitConverter.ToUInt32(header, (int)(offset2 + (i * 8) + 4));
                    }
                }

                for (int i = 0; i < fields.ThirdPartitions.Length; i++)
                {
                    if (offset1 + (i * 8) + 8 < 0x50000)
                    {
                        fields.FirstPartitions[i].Offset =
                            BigEndianBitConverter.ToUInt32(header, (int)(offset3 + (i * 8) + 0)) << 2;

                        fields.FirstPartitions[i].Type =
                            BigEndianBitConverter.ToUInt32(header, (int)(offset3 + (i * 8) + 4));
                    }
                }

                for (int i = 0; i < fields.FourthPartitions.Length; i++)
                {
                    if (offset1 + (i * 8) + 8 < 0x50000)
                    {
                        fields.FirstPartitions[i].Offset =
                            BigEndianBitConverter.ToUInt32(header, (int)(offset4 + (i * 8) + 0)) << 2;

                        fields.FirstPartitions[i].Type =
                            BigEndianBitConverter.ToUInt32(header, (int)(offset4 + (i * 8) + 4));
                    }
                }

                fields.Region       = header[0x4E000];
                fields.JapanAge     = header[0x4E010];
                fields.UsaAge       = header[0x4E011];
                fields.GermanAge    = header[0x4E013];
                fields.PegiAge      = header[0x4E014];
                fields.FinlandAge   = header[0x4E015];
                fields.PortugalAge  = header[0x4E016];
                fields.UkAge        = header[0x4E017];
                fields.AustraliaAge = header[0x4E018];
                fields.KoreaAge     = header[0x4E019];
            }
            else
            {
                fields.FirstPartitions  = new NintendoPartition[0];
                fields.SecondPartitions = new NintendoPartition[0];
                fields.ThirdPartitions  = new NintendoPartition[0];
                fields.FourthPartitions = new NintendoPartition[0];
            }

            AaruConsole.DebugWriteLine("Nintendo plugin", "discType = {0}", fields.DiscType);
            AaruConsole.DebugWriteLine("Nintendo plugin", "gameCode = {0}", fields.GameCode);
            AaruConsole.DebugWriteLine("Nintendo plugin", "regionCode = {0}", fields.RegionCode);
            AaruConsole.DebugWriteLine("Nintendo plugin", "publisherCode = {0}", fields.PublisherCode);
            AaruConsole.DebugWriteLine("Nintendo plugin", "discID = {0}", fields.DiscId);
            AaruConsole.DebugWriteLine("Nintendo plugin", "discNumber = {0}", fields.DiscNumber);
            AaruConsole.DebugWriteLine("Nintendo plugin", "discVersion = {0}", fields.DiscVersion);
            AaruConsole.DebugWriteLine("Nintendo plugin", "streaming = {0}", fields.Streaming);
            AaruConsole.DebugWriteLine("Nintendo plugin", "streamBufferSize = {0}", fields.StreamBufferSize);
            AaruConsole.DebugWriteLine("Nintendo plugin", "title = \"{0}\"", fields.Title);
            AaruConsole.DebugWriteLine("Nintendo plugin", "debugOff = 0x{0:X8}", fields.DebugOff);
            AaruConsole.DebugWriteLine("Nintendo plugin", "debugAddr = 0x{0:X8}", fields.DebugAddr);
            AaruConsole.DebugWriteLine("Nintendo plugin", "dolOff = 0x{0:X8}", fields.DolOff);
            AaruConsole.DebugWriteLine("Nintendo plugin", "fstOff = 0x{0:X8}", fields.FstOff);
            AaruConsole.DebugWriteLine("Nintendo plugin", "fstSize = {0}", fields.FstSize);
            AaruConsole.DebugWriteLine("Nintendo plugin", "fstMax = {0}", fields.FstMax);

            for (int i = 0; i < fields.FirstPartitions.Length; i++)
            {
                AaruConsole.DebugWriteLine("Nintendo plugin", "firstPartitions[{1}].offset = {0}",
                                           fields.FirstPartitions[i].Offset, i);

                AaruConsole.DebugWriteLine("Nintendo plugin", "firstPartitions[{1}].type = {0}",
                                           fields.FirstPartitions[i].Type, i);
            }

            for (int i = 0; i < fields.SecondPartitions.Length; i++)
            {
                AaruConsole.DebugWriteLine("Nintendo plugin", "secondPartitions[{1}].offset = {0}",
                                           fields.SecondPartitions[i].Offset, i);

                AaruConsole.DebugWriteLine("Nintendo plugin", "secondPartitions[{1}].type = {0}",
                                           fields.SecondPartitions[i].Type, i);
            }

            for (int i = 0; i < fields.ThirdPartitions.Length; i++)
            {
                AaruConsole.DebugWriteLine("Nintendo plugin", "thirdPartitions[{1}].offset = {0}",
                                           fields.ThirdPartitions[i].Offset, i);

                AaruConsole.DebugWriteLine("Nintendo plugin", "thirdPartitions[{1}].type = {0}",
                                           fields.ThirdPartitions[i].Type, i);
            }

            for (int i = 0; i < fields.FourthPartitions.Length; i++)
            {
                AaruConsole.DebugWriteLine("Nintendo plugin", "fourthPartitions[{1}].offset = {0}",
                                           fields.FourthPartitions[i].Offset, i);

                AaruConsole.DebugWriteLine("Nintendo plugin", "fourthPartitions[{1}].type = {0}",
                                           fields.FourthPartitions[i].Type, i);
            }

            AaruConsole.DebugWriteLine("Nintendo plugin", "region = {0}", fields.Region);
            AaruConsole.DebugWriteLine("Nintendo plugin", "japanAge = {0}", fields.JapanAge);
            AaruConsole.DebugWriteLine("Nintendo plugin", "usaAge = {0}", fields.UsaAge);
            AaruConsole.DebugWriteLine("Nintendo plugin", "germanAge = {0}", fields.GermanAge);
            AaruConsole.DebugWriteLine("Nintendo plugin", "pegiAge = {0}", fields.PegiAge);
            AaruConsole.DebugWriteLine("Nintendo plugin", "finlandAge = {0}", fields.FinlandAge);
            AaruConsole.DebugWriteLine("Nintendo plugin", "portugalAge = {0}", fields.PortugalAge);
            AaruConsole.DebugWriteLine("Nintendo plugin", "ukAge = {0}", fields.UkAge);
            AaruConsole.DebugWriteLine("Nintendo plugin", "australiaAge = {0}", fields.AustraliaAge);
            AaruConsole.DebugWriteLine("Nintendo plugin", "koreaAge = {0}", fields.KoreaAge);

            sbInformation.AppendLine("Nintendo optical filesystem");
            sbInformation.AppendLine(wii ? "Nintendo Wii Optical Disc" : "Nintendo GameCube Optical Disc");
            sbInformation.AppendFormat("Disc ID is {0}", fields.DiscId).AppendLine();
            sbInformation.AppendFormat("Disc is a {0} disc", DiscTypeToString(fields.DiscType)).AppendLine();
            sbInformation.AppendFormat("Disc region is {0}", RegionCodeToString(fields.RegionCode)).AppendLine();
            sbInformation.AppendFormat("Published by {0}", PublisherCodeToString(fields.PublisherCode)).AppendLine();

            if (fields.DiscNumber > 0)
            {
                sbInformation.AppendFormat("Disc number {0} of a multi-disc set", fields.DiscNumber + 1).AppendLine();
            }

            if (fields.Streaming)
            {
                sbInformation.AppendLine("Disc is prepared for audio streaming");
            }

            if (fields.StreamBufferSize > 0)
            {
                sbInformation.AppendFormat("Audio streaming buffer size is {0} bytes", fields.StreamBufferSize).
                AppendLine();
            }

            sbInformation.AppendFormat("Title: {0}", fields.Title).AppendLine();

            if (wii)
            {
                for (int i = 0; i < fields.FirstPartitions.Length; i++)
                {
                    sbInformation.AppendFormat("First {0} partition starts at sector {1}",
                                               PartitionTypeToString(fields.FirstPartitions[i].Type),
                                               fields.FirstPartitions[i].Offset / 2048).AppendLine();
                }

                for (int i = 0; i < fields.SecondPartitions.Length; i++)
                {
                    sbInformation.AppendFormat("Second {0} partition starts at sector {1}",
                                               PartitionTypeToString(fields.SecondPartitions[i].Type),
                                               fields.SecondPartitions[i].Offset / 2048).AppendLine();
                }

                for (int i = 0; i < fields.ThirdPartitions.Length; i++)
                {
                    sbInformation.AppendFormat("Third {0} partition starts at sector {1}",
                                               PartitionTypeToString(fields.ThirdPartitions[i].Type),
                                               fields.ThirdPartitions[i].Offset / 2048).AppendLine();
                }

                for (int i = 0; i < fields.FourthPartitions.Length; i++)
                {
                    sbInformation.AppendFormat("Fourth {0} partition starts at sector {1}",
                                               PartitionTypeToString(fields.FourthPartitions[i].Type),
                                               fields.FourthPartitions[i].Offset / 2048).AppendLine();
                }

                //                sbInformation.AppendFormat("Region byte is {0}", fields.region).AppendLine();
                if ((fields.JapanAge & 0x80) != 0x80)
                {
                    sbInformation.AppendFormat("Japan age rating is {0}", fields.JapanAge).AppendLine();
                }

                if ((fields.UsaAge & 0x80) != 0x80)
                {
                    sbInformation.AppendFormat("ESRB age rating is {0}", fields.UsaAge).AppendLine();
                }

                if ((fields.GermanAge & 0x80) != 0x80)
                {
                    sbInformation.AppendFormat("German age rating is {0}", fields.GermanAge).AppendLine();
                }

                if ((fields.PegiAge & 0x80) != 0x80)
                {
                    sbInformation.AppendFormat("PEGI age rating is {0}", fields.PegiAge).AppendLine();
                }

                if ((fields.FinlandAge & 0x80) != 0x80)
                {
                    sbInformation.AppendFormat("Finland age rating is {0}", fields.FinlandAge).AppendLine();
                }

                if ((fields.PortugalAge & 0x80) != 0x80)
                {
                    sbInformation.AppendFormat("Portugal age rating is {0}", fields.PortugalAge).AppendLine();
                }

                if ((fields.UkAge & 0x80) != 0x80)
                {
                    sbInformation.AppendFormat("UK age rating is {0}", fields.UkAge).AppendLine();
                }

                if ((fields.AustraliaAge & 0x80) != 0x80)
                {
                    sbInformation.AppendFormat("Australia age rating is {0}", fields.AustraliaAge).AppendLine();
                }

                if ((fields.KoreaAge & 0x80) != 0x80)
                {
                    sbInformation.AppendFormat("Korea age rating is {0}", fields.KoreaAge).AppendLine();
                }
            }
            else
            {
                sbInformation.AppendFormat("FST starts at {0} and has {1} bytes", fields.FstOff, fields.FstSize).
                AppendLine();
            }

            information            = sbInformation.ToString();
            XmlFsType.Bootable     = true;
            XmlFsType.Clusters     = (imagePlugin.Info.Sectors * imagePlugin.Info.SectorSize) / 2048;
            XmlFsType.ClusterSize  = 2048;
            XmlFsType.Type         = wii ? "Nintendo Wii filesystem" : "Nintendo Gamecube filesystem";
            XmlFsType.VolumeName   = fields.Title;
            XmlFsType.VolumeSerial = fields.DiscId;
        }
Пример #26
0
        public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information,
                                   Encoding encoding)
        {
            Encoding    = encoding ?? Encoding.GetEncoding("iso-8859-15");
            information = "";
            if (imagePlugin.Info.SectorSize < 512)
            {
                return;
            }

            uint sbAddr = REISER4_SUPER_OFFSET / imagePlugin.Info.SectorSize;

            if (sbAddr == 0)
            {
                sbAddr = 1;
            }

            Reiser4_Superblock reiserSb = new Reiser4_Superblock();

            uint sbSize = (uint)(Marshal.SizeOf(reiserSb) / imagePlugin.Info.SectorSize);

            if (Marshal.SizeOf(reiserSb) % imagePlugin.Info.SectorSize != 0)
            {
                sbSize++;
            }

            byte[] sector = imagePlugin.ReadSectors(partition.Start + sbAddr, sbSize);
            if (sector.Length < Marshal.SizeOf(reiserSb))
            {
                return;
            }

            IntPtr sbPtr = Marshal.AllocHGlobal(Marshal.SizeOf(reiserSb));

            Marshal.Copy(sector, 0, sbPtr, Marshal.SizeOf(reiserSb));
            reiserSb = (Reiser4_Superblock)Marshal.PtrToStructure(sbPtr, typeof(Reiser4_Superblock));
            Marshal.FreeHGlobal(sbPtr);

            if (!reiser4_magic.SequenceEqual(reiserSb.magic))
            {
                return;
            }

            StringBuilder sb = new StringBuilder();

            sb.AppendLine("Reiser 4 filesystem");
            sb.AppendFormat("{0} bytes per block", reiserSb.blocksize).AppendLine();
            sb.AppendFormat("Volume disk format: {0}", reiserSb.diskformat).AppendLine();
            sb.AppendFormat("Volume UUID: {0}", reiserSb.uuid).AppendLine();
            sb.AppendFormat("Volume name: {0}", StringHandlers.CToString(reiserSb.label, Encoding)).AppendLine();

            information = sb.ToString();

            XmlFsType = new FileSystemType
            {
                Type        = "Reiser 4 filesystem",
                ClusterSize = reiserSb.blocksize,
                Clusters    =
                    (long)((partition.End - partition.Start) * imagePlugin.Info.SectorSize / reiserSb.blocksize),
                VolumeName   = StringHandlers.CToString(reiserSb.label, Encoding),
                VolumeSerial = reiserSb.uuid.ToString()
            };
        }
Пример #27
0
        static BpbKind DetectBpbKind(byte[]                     bpbSector, IMediaImage imagePlugin,
                                     Partition partition,
                                     out BiosParameterBlockEbpb fakeBpb, out HumanParameterBlock humanBpb,
                                     out AtariParameterBlock atariBpb,
                                     out byte minBootNearJump,
                                     out bool andosOemCorrect, out bool bootable)
        {
            fakeBpb         = new BiosParameterBlockEbpb();
            minBootNearJump = 0;
            andosOemCorrect = false;
            bootable        = false;

            humanBpb = Marshal.ByteArrayToStructureBigEndian <HumanParameterBlock>(bpbSector);
            atariBpb = Marshal.ByteArrayToStructureLittleEndian <AtariParameterBlock>(bpbSector);

            ulong expectedClusters = humanBpb.bpc > 0 ? partition.Size / humanBpb.bpc : 0;

            // Check clusters for Human68k are correct
            bool humanClustersCorrect = humanBpb.clusters == 0
                                            ? humanBpb.big_clusters == expectedClusters
                                            : humanBpb.clusters == expectedClusters;

            // Check OEM for Human68k is correct
            bool humanOemCorrect = bpbSector[2] >= 0x20 && bpbSector[3] >= 0x20 && bpbSector[4] >= 0x20 &&
                                   bpbSector[5] >= 0x20 && bpbSector[6] >= 0x20 && bpbSector[7] >= 0x20 &&
                                   bpbSector[8] >= 0x20 && bpbSector[9] >= 0x20 && bpbSector[10] >= 0x20 &&
                                   bpbSector[11] >= 0x20 && bpbSector[12] >= 0x20 && bpbSector[13] >= 0x20 &&
                                   bpbSector[14] >= 0x20 && bpbSector[15] >= 0x20 && bpbSector[16] >= 0x20 &&
                                   bpbSector[17] >= 0x20;

            // Check correct branch for Human68k
            bool humanBranchCorrect = bpbSector[0] == 0x60 && bpbSector[1] >= 0x1C && bpbSector[1] < 0xFE;

            DicConsole.DebugWriteLine("FAT plugin", "humanClustersCorrect = {0}", humanClustersCorrect);
            DicConsole.DebugWriteLine("FAT plugin", "humanOemCorrect = {0}", humanOemCorrect);
            DicConsole.DebugWriteLine("FAT plugin", "humanBranchCorrect = {0}", humanBranchCorrect);

            // If all Human68k checks are correct, it is a Human68k FAT16
            bool useHumanBpb = humanClustersCorrect && humanOemCorrect && humanBranchCorrect && expectedClusters > 0;

            if (useHumanBpb)
            {
                DicConsole.DebugWriteLine("FAT plugin", "Using Human68k BPB");

                fakeBpb.jump        = humanBpb.jump;
                fakeBpb.oem_name    = humanBpb.oem_name;
                fakeBpb.bps         = (ushort)imagePlugin.Info.SectorSize;
                fakeBpb.spc         = (byte)(humanBpb.bpc / fakeBpb.bps);
                fakeBpb.fats_no     = 2;
                fakeBpb.root_ent    = humanBpb.root_ent;
                fakeBpb.media       = humanBpb.media;
                fakeBpb.spfat       = (ushort)(humanBpb.cpfat * fakeBpb.spc);
                fakeBpb.boot_code   = humanBpb.boot_code;
                fakeBpb.sectors     = humanBpb.clusters;
                fakeBpb.big_sectors = humanBpb.big_clusters;
                fakeBpb.rsectors    = 1;

                return(BpbKind.Human);
            }

            MsxParameterBlock           msxBpb        = new MsxParameterBlock();
            BiosParameterBlock2         dos2Bpb       = new BiosParameterBlock2();
            BiosParameterBlock30        dos30Bpb      = new BiosParameterBlock30();
            BiosParameterBlock32        dos32Bpb      = new BiosParameterBlock32();
            BiosParameterBlock33        dos33Bpb      = new BiosParameterBlock33();
            BiosParameterBlockShortEbpb shortEbpb     = new BiosParameterBlockShortEbpb();
            BiosParameterBlockEbpb      ebpb          = new BiosParameterBlockEbpb();
            Fat32ParameterBlockShort    shortFat32Bpb = new Fat32ParameterBlockShort();
            Fat32ParameterBlock         fat32Bpb      = new Fat32ParameterBlock();
            ApricotLabel apricotBpb = new ApricotLabel();

            bool useAtariBpb          = false;
            bool useMsxBpb            = false;
            bool useDos2Bpb           = false;
            bool useDos3Bpb           = false;
            bool useDos32Bpb          = false;
            bool useDos33Bpb          = false;
            bool userShortExtendedBpb = false;
            bool useExtendedBpb       = false;
            bool useShortFat32        = false;
            bool useLongFat32         = false;
            bool useApricotBpb        = false;
            bool useDecRainbowBpb     = false;

            if (imagePlugin.Info.SectorSize >= 256 && !useHumanBpb)
            {
                msxBpb        = Marshal.ByteArrayToStructureLittleEndian <MsxParameterBlock>(bpbSector);
                dos2Bpb       = Marshal.ByteArrayToStructureLittleEndian <BiosParameterBlock2>(bpbSector);
                dos30Bpb      = Marshal.ByteArrayToStructureLittleEndian <BiosParameterBlock30>(bpbSector);
                dos32Bpb      = Marshal.ByteArrayToStructureLittleEndian <BiosParameterBlock32>(bpbSector);
                dos33Bpb      = Marshal.ByteArrayToStructureLittleEndian <BiosParameterBlock33>(bpbSector);
                shortEbpb     = Marshal.ByteArrayToStructureLittleEndian <BiosParameterBlockShortEbpb>(bpbSector);
                ebpb          = Marshal.ByteArrayToStructureLittleEndian <BiosParameterBlockEbpb>(bpbSector);
                shortFat32Bpb = Marshal.ByteArrayToStructureLittleEndian <Fat32ParameterBlockShort>(bpbSector);
                fat32Bpb      = Marshal.ByteArrayToStructureLittleEndian <Fat32ParameterBlock>(bpbSector);
                apricotBpb    = Marshal.ByteArrayToStructureLittleEndian <ApricotLabel>(bpbSector);

                int bitsInBpsMsx        = CountBits.Count(msxBpb.bps);
                int bitsInBpsDos33      = CountBits.Count(dos33Bpb.bps);
                int bitsInBpsDos40      = CountBits.Count(ebpb.bps);
                int bitsInBpsFat32Short = CountBits.Count(shortFat32Bpb.bps);
                int bitsInBpsFat32      = CountBits.Count(fat32Bpb.bps);
                int bitsInBpsApricot    = CountBits.Count(apricotBpb.mainBPB.bps);

                bool correctSpcMsx = msxBpb.spc == 1 || msxBpb.spc == 2 || msxBpb.spc == 4 || msxBpb.spc == 8 ||
                                     msxBpb.spc == 16 || msxBpb.spc == 32 || msxBpb.spc == 64;
                bool correctSpcDos33 = dos33Bpb.spc == 1 || dos33Bpb.spc == 2 || dos33Bpb.spc == 4 ||
                                       dos33Bpb.spc == 8 || dos33Bpb.spc == 16 || dos33Bpb.spc == 32 ||
                                       dos33Bpb.spc == 64;
                bool correctSpcDos40 = ebpb.spc == 1 || ebpb.spc == 2 || ebpb.spc == 4 || ebpb.spc == 8 ||
                                       ebpb.spc == 16 || ebpb.spc == 32 || ebpb.spc == 64;
                bool correctSpcFat32Short = shortFat32Bpb.spc == 1 || shortFat32Bpb.spc == 2 ||
                                            shortFat32Bpb.spc == 4 || shortFat32Bpb.spc == 8 ||
                                            shortFat32Bpb.spc == 16 || shortFat32Bpb.spc == 32 ||
                                            shortFat32Bpb.spc == 64;
                bool correctSpcFat32 = fat32Bpb.spc == 1 || fat32Bpb.spc == 2 || fat32Bpb.spc == 4 ||
                                       fat32Bpb.spc == 8 || fat32Bpb.spc == 16 || fat32Bpb.spc == 32 ||
                                       fat32Bpb.spc == 64;
                bool correctSpcApricot = apricotBpb.mainBPB.spc == 1 || apricotBpb.mainBPB.spc == 2 ||
                                         apricotBpb.mainBPB.spc == 4 || apricotBpb.mainBPB.spc == 8 ||
                                         apricotBpb.mainBPB.spc == 16 || apricotBpb.mainBPB.spc == 32 ||
                                         apricotBpb.mainBPB.spc == 64;

                // This is to support FAT partitions on hybrid ISO/USB images
                if (imagePlugin.Info.XmlMediaType == XmlMediaType.OpticalDisc)
                {
                    atariBpb.sectors           /= 4;
                    msxBpb.sectors             /= 4;
                    dos2Bpb.sectors            /= 4;
                    dos30Bpb.sectors           /= 4;
                    dos32Bpb.sectors           /= 4;
                    dos33Bpb.sectors           /= 4;
                    dos33Bpb.big_sectors       /= 4;
                    shortEbpb.sectors          /= 4;
                    shortEbpb.big_sectors      /= 4;
                    ebpb.sectors               /= 4;
                    ebpb.big_sectors           /= 4;
                    shortFat32Bpb.sectors      /= 4;
                    shortFat32Bpb.big_sectors  /= 4;
                    shortFat32Bpb.huge_sectors /= 4;
                    fat32Bpb.sectors           /= 4;
                    fat32Bpb.big_sectors       /= 4;
                    apricotBpb.mainBPB.sectors /= 4;
                }

                andosOemCorrect = dos33Bpb.oem_name[0] < 0x20 && dos33Bpb.oem_name[1] >= 0x20 &&
                                  dos33Bpb.oem_name[2] >= 0x20 && dos33Bpb.oem_name[3] >= 0x20 &&
                                  dos33Bpb.oem_name[4] >= 0x20 && dos33Bpb.oem_name[5] >= 0x20 &&
                                  dos33Bpb.oem_name[6] >= 0x20 && dos33Bpb.oem_name[7] >= 0x20;

                if (bitsInBpsFat32 == 1 && correctSpcFat32 && fat32Bpb.fats_no <= 2 &&
                    fat32Bpb.sectors == 0 &&
                    fat32Bpb.spfat == 0 && fat32Bpb.signature == 0x29 &&
                    Encoding.ASCII.GetString(fat32Bpb.fs_type) == "FAT32   ")
                {
                    DicConsole.DebugWriteLine("FAT plugin", "Using FAT32 BPB");
                    useLongFat32    = true;
                    minBootNearJump = 0x58;
                    return(BpbKind.LongFat32);
                }

                if (bitsInBpsFat32Short == 1 && correctSpcFat32Short && shortFat32Bpb.fats_no <= 2 &&
                    shortFat32Bpb.sectors == 0 && shortFat32Bpb.spfat == 0 &&
                    shortFat32Bpb.signature == 0x28)
                {
                    DicConsole.DebugWriteLine("FAT plugin", "Using short FAT32 BPB");
                    useShortFat32 = shortFat32Bpb.big_sectors == 0
                                        ? shortFat32Bpb.huge_sectors <= partition.End - partition.Start + 1
                                        : shortFat32Bpb.big_sectors <= partition.End - partition.Start + 1;
                    minBootNearJump = 0x57;
                    return(BpbKind.ShortFat32);
                }

                if (bitsInBpsMsx == 1 && correctSpcMsx &&
                    msxBpb.fats_no <= 2 && msxBpb.root_ent > 0 &&
                    msxBpb.sectors <= partition.End - partition.Start + 1 &&
                    msxBpb.spfat > 0 &&
                    Encoding.ASCII.GetString(msxBpb.vol_id) == "VOL_ID")
                {
                    DicConsole.DebugWriteLine("FAT plugin", "Using MSX BPB");
                    useMsxBpb = true;
                }
                else if (bitsInBpsApricot == 1 && correctSpcApricot &&
                         apricotBpb.mainBPB.fats_no <= 2 &&
                         apricotBpb.mainBPB.root_ent > 0 &&
                         apricotBpb.mainBPB.sectors <= partition.End - partition.Start + 1 &&
                         apricotBpb.mainBPB.spfat > 0 &&
                         apricotBpb.partitionCount == 0)
                {
                    DicConsole.DebugWriteLine("FAT plugin", "Using Apricot BPB");
                    useApricotBpb = true;
                }
                else if (bitsInBpsDos40 == 1 && correctSpcDos40 && ebpb.fats_no <= 2 && ebpb.root_ent > 0 &&
                         ebpb.spfat > 0 && (ebpb.signature == 0x28 || ebpb.signature == 0x29 || andosOemCorrect))
                {
                    if (ebpb.sectors == 0)
                    {
                        if (ebpb.big_sectors <= partition.End - partition.Start + 1)
                        {
                            if (ebpb.signature == 0x29 || andosOemCorrect)
                            {
                                DicConsole.DebugWriteLine("FAT plugin", "Using DOS 4.0 BPB");
                                useExtendedBpb  = true;
                                minBootNearJump = 0x3C;
                            }
                            else
                            {
                                DicConsole.DebugWriteLine("FAT plugin", "Using DOS 3.4 BPB");
                                userShortExtendedBpb = true;
                                minBootNearJump      = 0x29;
                            }
                        }
                    }
                    else if (ebpb.sectors <= partition.End - partition.Start + 1)
                    {
                        if (ebpb.signature == 0x29 || andosOemCorrect)
                        {
                            DicConsole.DebugWriteLine("FAT plugin", "Using DOS 4.0 BPB");
                            useExtendedBpb  = true;
                            minBootNearJump = 0x3C;
                        }
                        else
                        {
                            DicConsole.DebugWriteLine("FAT plugin", "Using DOS 3.4 BPB");
                            userShortExtendedBpb = true;
                            minBootNearJump      = 0x29;
                        }
                    }
                }
                else if (bitsInBpsDos33 == 1 && correctSpcDos33 &&
                         dos33Bpb.rsectors < partition.End - partition.Start &&
                         dos33Bpb.fats_no <= 2 &&
                         dos33Bpb.root_ent > 0 && dos33Bpb.spfat > 0)
                {
                    if (dos33Bpb.sectors == 0 && dos33Bpb.hsectors <= partition.Start && dos33Bpb.big_sectors > 0 &&
                        dos33Bpb.big_sectors <= partition.End - partition.Start + 1)
                    {
                        DicConsole.DebugWriteLine("FAT plugin", "Using DOS 3.3 BPB");
                        useDos33Bpb     = true;
                        minBootNearJump = 0x22;
                    }
                    else if (dos33Bpb.big_sectors == 0 && dos33Bpb.hsectors <= partition.Start && dos33Bpb.sectors > 0 &&
                             dos33Bpb.sectors <= partition.End - partition.Start + 1)
                    {
                        if (atariBpb.jump[0] == 0x60 || atariBpb.jump[0] == 0xE9 && atariBpb.jump[1] == 0x00 &&
                            Encoding.ASCII.GetString(dos33Bpb.oem_name) != "NEXT    ")
                        {
                            DicConsole.DebugWriteLine("FAT plugin", "Using Atari BPB");
                            useAtariBpb = true;
                        }
                        else
                        {
                            DicConsole.DebugWriteLine("FAT plugin", "Using DOS 3.3 BPB");
                            useDos33Bpb     = true;
                            minBootNearJump = 0x22;
                        }
                    }
                    else
                    {
                        if (dos32Bpb.hsectors <= partition.Start &&
                            dos32Bpb.hsectors + dos32Bpb.sectors == dos32Bpb.total_sectors)
                        {
                            DicConsole.DebugWriteLine("FAT plugin", "Using DOS 3.2 BPB");
                            useDos32Bpb     = true;
                            minBootNearJump = 0x1E;
                        }
                        else if (dos30Bpb.sptrk > 0 && dos30Bpb.sptrk < 64 && dos30Bpb.heads > 0 && dos30Bpb.heads < 256)
                        {
                            if (atariBpb.jump[0] == 0x60 || atariBpb.jump[0] == 0xE9 && atariBpb.jump[1] == 0x00 &&
                                Encoding.ASCII.GetString(dos33Bpb.oem_name) != "NEXT    ")
                            {
                                DicConsole.DebugWriteLine("FAT plugin", "Using Atari BPB");
                                useAtariBpb = true;
                            }
                            else
                            {
                                DicConsole.DebugWriteLine("FAT plugin", "Using DOS 3.0 BPB");
                                useDos3Bpb      = true;
                                minBootNearJump = 0x1C;
                            }
                        }
                        else
                        {
                            if (atariBpb.jump[0] == 0x60 || atariBpb.jump[0] == 0xE9 && atariBpb.jump[1] == 0x00 &&
                                Encoding.ASCII.GetString(dos33Bpb.oem_name) != "NEXT    ")
                            {
                                DicConsole.DebugWriteLine("FAT plugin", "Using Atari BPB");
                                useAtariBpb = true;
                            }
                            else
                            {
                                DicConsole.DebugWriteLine("FAT plugin", "Using DOS 2.0 BPB");
                                useDos2Bpb      = true;
                                minBootNearJump = 0x16;
                            }
                        }
                    }
                }
            }

            // DEC Rainbow, lacks a BPB but has a very concrete structure...
            if (imagePlugin.Info.Sectors == 800 && imagePlugin.Info.SectorSize == 512 && !useAtariBpb && !useMsxBpb &&
                !useDos2Bpb && !useDos3Bpb && !useDos32Bpb && !useDos33Bpb &&
                !userShortExtendedBpb && !useExtendedBpb &&
                !useHumanBpb && !useShortFat32 && !useLongFat32 && !useApricotBpb)
            {
                // DEC Rainbow boots up with a Z80, first byte should be DI (disable interrupts)
                byte z80Di = bpbSector[0];
                // First FAT1 sector resides at LBA 0x14
                byte[] fat1Sector0 = imagePlugin.ReadSector(0x14);
                // First FAT2 sector resides at LBA 0x1A
                byte[] fat2Sector0 = imagePlugin.ReadSector(0x1A);
                bool   equalFatIds = fat1Sector0[0] == fat2Sector0[0] && fat1Sector0[1] == fat2Sector0[1];
                // Volume is software interleaved 2:1
                MemoryStream rootMs = new MemoryStream();
                foreach (byte[] tmp in from ulong rootSector in new[] { 0x17, 0x19, 0x1B, 0x1D, 0x1E, 0x20 }
                         select imagePlugin.ReadSector(rootSector))
                {
                    rootMs.Write(tmp, 0, tmp.Length);
                }

                byte[] rootDir      = rootMs.ToArray();
                bool   validRootDir = true;

                // Iterate all root directory
                for (int e = 0; e < 96 * 32; e += 32)
                {
                    for (int c = 0; c < 11; c++)
                    {
                        if (rootDir[c + e] < 0x20 && rootDir[c + e] != 0x00 && rootDir[c + e] != 0x05 ||
                            rootDir[c + e] == 0xFF ||
                            rootDir[c + e] == 0x2E)
                        {
                            validRootDir = false;
                            break;
                        }
                    }

                    if (!validRootDir)
                    {
                        break;
                    }
                }

                if (z80Di == 0xF3 && equalFatIds && (fat1Sector0[0] & 0xF0) == 0xF0 && fat1Sector0[1] == 0xFF &&
                    validRootDir)
                {
                    useDecRainbowBpb = true;
                    DicConsole.DebugWriteLine("FAT plugin", "Using DEC Rainbow hardcoded BPB.");
                    fakeBpb.bps       = 512;
                    fakeBpb.spc       = 1;
                    fakeBpb.rsectors  = 20;
                    fakeBpb.fats_no   = 2;
                    fakeBpb.root_ent  = 96;
                    fakeBpb.sectors   = 800;
                    fakeBpb.media     = 0xFA;
                    fakeBpb.sptrk     = 10;
                    fakeBpb.heads     = 1;
                    fakeBpb.hsectors  = 0;
                    fakeBpb.spfat     = 3;
                    bootable          = true;
                    fakeBpb.boot_code = bpbSector;
                    return(BpbKind.DecRainbow);
                }
            }

            if (!useAtariBpb && !useMsxBpb && !useDos2Bpb && !useDos3Bpb && !useDos32Bpb &&
                !useDos33Bpb &&
                !useHumanBpb && !userShortExtendedBpb && !useExtendedBpb && !useShortFat32 && !useLongFat32 &&
                !useApricotBpb && !useDecRainbowBpb)
            {
                byte[] fatSector = imagePlugin.ReadSector(1 + partition.Start);
                switch (fatSector[0])
                {
                case 0xE5:
                    if (imagePlugin.Info.Sectors == 2002 && imagePlugin.Info.SectorSize == 128)
                    {
                        DicConsole.DebugWriteLine("FAT plugin", "Using hardcoded BPB.");
                        fakeBpb.bps      = 128;
                        fakeBpb.spc      = 4;
                        fakeBpb.rsectors = 1;
                        fakeBpb.fats_no  = 2;
                        fakeBpb.root_ent = 64;
                        fakeBpb.sectors  = 2002;
                        fakeBpb.media    = 0xE5;
                        fakeBpb.sptrk    = 26;
                        fakeBpb.heads    = 1;
                        fakeBpb.hsectors = 0;
                        fakeBpb.spfat    = 1;
                    }

                    break;

                case 0xFD:
                    if (imagePlugin.Info.Sectors == 4004 && imagePlugin.Info.SectorSize == 128)
                    {
                        DicConsole.DebugWriteLine("FAT plugin", "Using hardcoded BPB.");
                        fakeBpb.bps      = 128;
                        fakeBpb.spc      = 4;
                        fakeBpb.rsectors = 4;
                        fakeBpb.fats_no  = 2;
                        fakeBpb.root_ent = 68;
                        fakeBpb.sectors  = 4004;
                        fakeBpb.media    = 0xFD;
                        fakeBpb.sptrk    = 26;
                        fakeBpb.heads    = 2;
                        fakeBpb.hsectors = 0;
                        fakeBpb.spfat    = 6;
                    }
                    else if (imagePlugin.Info.Sectors == 2002 && imagePlugin.Info.SectorSize == 128)
                    {
                        DicConsole.DebugWriteLine("FAT plugin", "Using hardcoded BPB.");
                        fakeBpb.bps      = 128;
                        fakeBpb.spc      = 4;
                        fakeBpb.rsectors = 4;
                        fakeBpb.fats_no  = 2;
                        fakeBpb.root_ent = 68;
                        fakeBpb.sectors  = 2002;
                        fakeBpb.media    = 0xFD;
                        fakeBpb.sptrk    = 26;
                        fakeBpb.heads    = 1;
                        fakeBpb.hsectors = 0;
                        fakeBpb.spfat    = 6;
                    }

                    break;

                case 0xFE:
                    if (imagePlugin.Info.Sectors == 320 && imagePlugin.Info.SectorSize == 512)
                    {
                        DicConsole.DebugWriteLine("FAT plugin", "Using hardcoded BPB for 5.25\" SSDD.");
                        fakeBpb.bps      = 512;
                        fakeBpb.spc      = 1;
                        fakeBpb.rsectors = 1;
                        fakeBpb.fats_no  = 2;
                        fakeBpb.root_ent = 64;
                        fakeBpb.sectors  = 320;
                        fakeBpb.media    = 0xFE;
                        fakeBpb.sptrk    = 8;
                        fakeBpb.heads    = 1;
                        fakeBpb.hsectors = 0;
                        fakeBpb.spfat    = 1;
                    }
                    else if (imagePlugin.Info.Sectors == 2002 && imagePlugin.Info.SectorSize == 128)
                    {
                        DicConsole.DebugWriteLine("FAT plugin", "Using hardcoded BPB.");
                        fakeBpb.bps      = 128;
                        fakeBpb.spc      = 4;
                        fakeBpb.rsectors = 1;
                        fakeBpb.fats_no  = 2;
                        fakeBpb.root_ent = 68;
                        fakeBpb.sectors  = 2002;
                        fakeBpb.media    = 0xFE;
                        fakeBpb.sptrk    = 26;
                        fakeBpb.heads    = 1;
                        fakeBpb.hsectors = 0;
                        fakeBpb.spfat    = 6;
                    }
                    else if (imagePlugin.Info.Sectors == 1232 && imagePlugin.Info.SectorSize == 1024)
                    {
                        DicConsole.DebugWriteLine("FAT plugin", "Using hardcoded BPB.");
                        fakeBpb.bps      = 1024;
                        fakeBpb.spc      = 1;
                        fakeBpb.rsectors = 1;
                        fakeBpb.fats_no  = 2;
                        fakeBpb.root_ent = 192;
                        fakeBpb.sectors  = 1232;
                        fakeBpb.media    = 0xFE;
                        fakeBpb.sptrk    = 8;
                        fakeBpb.heads    = 2;
                        fakeBpb.hsectors = 0;
                        fakeBpb.spfat    = 2;
                    }
                    else if (imagePlugin.Info.Sectors == 616 && imagePlugin.Info.SectorSize == 1024)
                    {
                        DicConsole.DebugWriteLine("FAT plugin", "Using hardcoded BPB.");
                        fakeBpb.bps      = 1024;
                        fakeBpb.spc      = 1;
                        fakeBpb.rsectors = 1;
                        fakeBpb.fats_no  = 2;
                        fakeBpb.root_ent = 6192;
                        fakeBpb.sectors  = 616;
                        fakeBpb.media    = 0xFE;
                        fakeBpb.sptrk    = 8;
                        fakeBpb.heads    = 2;
                        fakeBpb.hsectors = 0;
                    }
                    else if (imagePlugin.Info.Sectors == 720 && imagePlugin.Info.SectorSize == 128)
                    {
                        DicConsole.DebugWriteLine("FAT plugin", "Using hardcoded BPB.");
                        fakeBpb.bps      = 128;
                        fakeBpb.spc      = 2;
                        fakeBpb.rsectors = 54;
                        fakeBpb.fats_no  = 2;
                        fakeBpb.root_ent = 64;
                        fakeBpb.sectors  = 720;
                        fakeBpb.media    = 0xFE;
                        fakeBpb.sptrk    = 18;
                        fakeBpb.heads    = 1;
                        fakeBpb.hsectors = 0;
                        fakeBpb.spfat    = 4;
                    }
                    else if (imagePlugin.Info.Sectors == 640 && imagePlugin.Info.SectorSize == 512)
                    {
                        DicConsole.DebugWriteLine("FAT plugin", "Using hardcoded BPB for 5.25\" DSDD.");
                        fakeBpb.bps      = 512;
                        fakeBpb.spc      = 2;
                        fakeBpb.rsectors = 1;
                        fakeBpb.fats_no  = 2;
                        fakeBpb.root_ent = 112;
                        fakeBpb.sectors  = 640;
                        fakeBpb.media    = 0xFF;
                        fakeBpb.sptrk    = 8;
                        fakeBpb.heads    = 2;
                        fakeBpb.hsectors = 0;
                        fakeBpb.spfat    = 1;
                    }

                    break;

                case 0xFF:
                    if (imagePlugin.Info.Sectors == 640 && imagePlugin.Info.SectorSize == 512)
                    {
                        DicConsole.DebugWriteLine("FAT plugin", "Using hardcoded BPB for 5.25\" DSDD.");
                        fakeBpb.bps      = 512;
                        fakeBpb.spc      = 2;
                        fakeBpb.rsectors = 1;
                        fakeBpb.fats_no  = 2;
                        fakeBpb.root_ent = 112;
                        fakeBpb.sectors  = 640;
                        fakeBpb.media    = 0xFF;
                        fakeBpb.sptrk    = 8;
                        fakeBpb.heads    = 2;
                        fakeBpb.hsectors = 0;
                        fakeBpb.spfat    = 1;
                    }

                    break;
                }

                // This assumes a bootable sector will jump somewhere or disable interrupts in x86 code
                bootable |= bpbSector[0] == 0xFA ||
                            bpbSector[0] == 0xEB && bpbSector[1] <= 0x7F ||
                            bpbSector[0] == 0xE9 &&
                            BitConverter.ToUInt16(bpbSector, 1) <= 0x1FC;
                fakeBpb.boot_code = bpbSector;
                return(BpbKind.Hardcoded);
            }

            if (useExtendedBpb)
            {
                fakeBpb = ebpb;
                return(BpbKind.Extended);
            }

            if (userShortExtendedBpb)
            {
                fakeBpb.jump           = shortEbpb.jump;
                fakeBpb.oem_name       = shortEbpb.oem_name;
                fakeBpb.bps            = shortEbpb.bps;
                fakeBpb.spc            = shortEbpb.spc;
                fakeBpb.rsectors       = shortEbpb.rsectors;
                fakeBpb.fats_no        = shortEbpb.fats_no;
                fakeBpb.root_ent       = shortEbpb.root_ent;
                fakeBpb.sectors        = shortEbpb.sectors;
                fakeBpb.media          = shortEbpb.media;
                fakeBpb.spfat          = shortEbpb.spfat;
                fakeBpb.sptrk          = shortEbpb.sptrk;
                fakeBpb.heads          = shortEbpb.heads;
                fakeBpb.hsectors       = shortEbpb.hsectors;
                fakeBpb.big_sectors    = shortEbpb.big_sectors;
                fakeBpb.drive_no       = shortEbpb.drive_no;
                fakeBpb.flags          = shortEbpb.flags;
                fakeBpb.signature      = shortEbpb.signature;
                fakeBpb.serial_no      = shortEbpb.serial_no;
                fakeBpb.boot_code      = shortEbpb.boot_code;
                fakeBpb.boot_signature = shortEbpb.boot_signature;
                return(BpbKind.ShortExtended);
            }

            if (useDos33Bpb)
            {
                fakeBpb.jump           = dos33Bpb.jump;
                fakeBpb.oem_name       = dos33Bpb.oem_name;
                fakeBpb.bps            = dos33Bpb.bps;
                fakeBpb.spc            = dos33Bpb.spc;
                fakeBpb.rsectors       = dos33Bpb.rsectors;
                fakeBpb.fats_no        = dos33Bpb.fats_no;
                fakeBpb.root_ent       = dos33Bpb.root_ent;
                fakeBpb.sectors        = dos33Bpb.sectors;
                fakeBpb.media          = dos33Bpb.media;
                fakeBpb.spfat          = dos33Bpb.spfat;
                fakeBpb.sptrk          = dos33Bpb.sptrk;
                fakeBpb.heads          = dos33Bpb.heads;
                fakeBpb.hsectors       = dos33Bpb.hsectors;
                fakeBpb.big_sectors    = dos33Bpb.big_sectors;
                fakeBpb.boot_code      = dos33Bpb.boot_code;
                fakeBpb.boot_signature = dos33Bpb.boot_signature;
                return(BpbKind.Dos33);
            }

            if (useDos32Bpb)
            {
                fakeBpb.jump           = dos32Bpb.jump;
                fakeBpb.oem_name       = dos32Bpb.oem_name;
                fakeBpb.bps            = dos32Bpb.bps;
                fakeBpb.spc            = dos32Bpb.spc;
                fakeBpb.rsectors       = dos32Bpb.rsectors;
                fakeBpb.fats_no        = dos32Bpb.fats_no;
                fakeBpb.root_ent       = dos32Bpb.root_ent;
                fakeBpb.sectors        = dos32Bpb.sectors;
                fakeBpb.media          = dos32Bpb.media;
                fakeBpb.spfat          = dos32Bpb.spfat;
                fakeBpb.sptrk          = dos32Bpb.sptrk;
                fakeBpb.heads          = dos32Bpb.heads;
                fakeBpb.hsectors       = dos32Bpb.hsectors;
                fakeBpb.boot_code      = dos32Bpb.boot_code;
                fakeBpb.boot_signature = dos32Bpb.boot_signature;
                return(BpbKind.Dos32);
            }

            if (useDos3Bpb)
            {
                fakeBpb.jump           = dos30Bpb.jump;
                fakeBpb.oem_name       = dos30Bpb.oem_name;
                fakeBpb.bps            = dos30Bpb.bps;
                fakeBpb.spc            = dos30Bpb.spc;
                fakeBpb.rsectors       = dos30Bpb.rsectors;
                fakeBpb.fats_no        = dos30Bpb.fats_no;
                fakeBpb.root_ent       = dos30Bpb.root_ent;
                fakeBpb.sectors        = dos30Bpb.sectors;
                fakeBpb.media          = dos30Bpb.media;
                fakeBpb.spfat          = dos30Bpb.spfat;
                fakeBpb.sptrk          = dos30Bpb.sptrk;
                fakeBpb.heads          = dos30Bpb.heads;
                fakeBpb.hsectors       = dos30Bpb.hsectors;
                fakeBpb.boot_code      = dos30Bpb.boot_code;
                fakeBpb.boot_signature = dos30Bpb.boot_signature;
                return(BpbKind.Dos3);
            }

            if (useDos2Bpb)
            {
                fakeBpb.jump           = dos2Bpb.jump;
                fakeBpb.oem_name       = dos2Bpb.oem_name;
                fakeBpb.bps            = dos2Bpb.bps;
                fakeBpb.spc            = dos2Bpb.spc;
                fakeBpb.rsectors       = dos2Bpb.rsectors;
                fakeBpb.fats_no        = dos2Bpb.fats_no;
                fakeBpb.root_ent       = dos2Bpb.root_ent;
                fakeBpb.sectors        = dos2Bpb.sectors;
                fakeBpb.media          = dos2Bpb.media;
                fakeBpb.spfat          = dos2Bpb.spfat;
                fakeBpb.boot_code      = dos2Bpb.boot_code;
                fakeBpb.boot_signature = dos2Bpb.boot_signature;
                return(BpbKind.Dos2);
            }

            if (useMsxBpb)
            {
                fakeBpb.jump           = msxBpb.jump;
                fakeBpb.oem_name       = msxBpb.oem_name;
                fakeBpb.bps            = msxBpb.bps;
                fakeBpb.spc            = msxBpb.spc;
                fakeBpb.rsectors       = msxBpb.rsectors;
                fakeBpb.fats_no        = msxBpb.fats_no;
                fakeBpb.root_ent       = msxBpb.root_ent;
                fakeBpb.sectors        = msxBpb.sectors;
                fakeBpb.media          = msxBpb.media;
                fakeBpb.spfat          = msxBpb.spfat;
                fakeBpb.sptrk          = msxBpb.sptrk;
                fakeBpb.heads          = msxBpb.heads;
                fakeBpb.hsectors       = msxBpb.hsectors;
                fakeBpb.boot_code      = msxBpb.boot_code;
                fakeBpb.boot_signature = msxBpb.boot_signature;
                fakeBpb.serial_no      = msxBpb.serial_no;
                // TODO: Is there any way to check this?
                bootable = true;
                return(BpbKind.Msx);
            }

            if (useAtariBpb)
            {
                fakeBpb.jump      = atariBpb.jump;
                fakeBpb.oem_name  = atariBpb.oem_name;
                fakeBpb.bps       = atariBpb.bps;
                fakeBpb.spc       = atariBpb.spc;
                fakeBpb.rsectors  = atariBpb.rsectors;
                fakeBpb.fats_no   = atariBpb.fats_no;
                fakeBpb.root_ent  = atariBpb.root_ent;
                fakeBpb.sectors   = atariBpb.sectors;
                fakeBpb.media     = atariBpb.media;
                fakeBpb.spfat     = atariBpb.spfat;
                fakeBpb.sptrk     = atariBpb.sptrk;
                fakeBpb.heads     = atariBpb.heads;
                fakeBpb.boot_code = atariBpb.boot_code;
                return(BpbKind.Atari);
            }

            if (useApricotBpb)
            {
                fakeBpb.bps      = apricotBpb.mainBPB.bps;
                fakeBpb.spc      = apricotBpb.mainBPB.spc;
                fakeBpb.rsectors = apricotBpb.mainBPB.rsectors;
                fakeBpb.fats_no  = apricotBpb.mainBPB.fats_no;
                fakeBpb.root_ent = apricotBpb.mainBPB.root_ent;
                fakeBpb.sectors  = apricotBpb.mainBPB.sectors;
                fakeBpb.media    = apricotBpb.mainBPB.media;
                fakeBpb.spfat    = apricotBpb.mainBPB.spfat;
                fakeBpb.sptrk    = apricotBpb.spt;
                bootable         = apricotBpb.bootType > 0;

                if (apricotBpb.bootLocation > 0 &&
                    apricotBpb.bootLocation + apricotBpb.bootSize < imagePlugin.Info.Sectors)
                {
                    fakeBpb.boot_code = imagePlugin.ReadSectors(apricotBpb.bootLocation,
                                                                (uint)(apricotBpb.sectorSize * apricotBpb.bootSize) /
                                                                imagePlugin.Info.SectorSize);
                }
                return(BpbKind.Apricot);
            }

            return(BpbKind.None);
        }
Пример #28
0
        public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information,
                                   Encoding encoding)
        {
            Encoding    = Encoding.BigEndianUnicode;
            information = "";

            var vh = new VolumeHeader();

            ulong hfspOffset;
            bool  wrapped;

            uint sectorsToRead = 0x800 / imagePlugin.Info.SectorSize;

            if (0x800 % imagePlugin.Info.SectorSize > 0)
            {
                sectorsToRead++;
            }

            byte[] vhSector = imagePlugin.ReadSectors(partition.Start, sectorsToRead);

            ushort drSigWord = BigEndianBitConverter.ToUInt16(vhSector, 0x400);

            if (drSigWord == AppleCommon.HFS_MAGIC)                          // "BD"
            {
                drSigWord = BigEndianBitConverter.ToUInt16(vhSector, 0x47C); // Read embedded HFS+ signature

                if (drSigWord == AppleCommon.HFSP_MAGIC)                     // "H+"
                {
                    ushort xdrStABNt = BigEndianBitConverter.ToUInt16(vhSector, 0x47E);

                    uint drAlBlkSiz = BigEndianBitConverter.ToUInt32(vhSector, 0x414);

                    ushort drAlBlSt = BigEndianBitConverter.ToUInt16(vhSector, 0x41C);

                    hfspOffset = (ulong)(((drAlBlSt * 512) + (xdrStABNt * drAlBlkSiz)) / imagePlugin.Info.SectorSize);
                    wrapped    = true;
                }
                else
                {
                    hfspOffset = 0;
                    wrapped    = false;
                }
            }
            else
            {
                hfspOffset = 0;
                wrapped    = false;
            }

            vhSector = imagePlugin.ReadSectors(partition.Start + hfspOffset, sectorsToRead); // Read volume header

            vh.signature = BigEndianBitConverter.ToUInt16(vhSector, 0x400);

            if (vh.signature != AppleCommon.HFSP_MAGIC &&
                vh.signature != AppleCommon.HFSX_MAGIC)
            {
                return;
            }

            var sb = new StringBuilder();

            if (vh.signature == 0x482B)
            {
                sb.AppendLine("HFS+ filesystem.");
            }

            if (vh.signature == 0x4858)
            {
                sb.AppendLine("HFSX filesystem.");
            }

            if (wrapped)
            {
                sb.AppendLine("Volume is wrapped inside an HFS volume.");
            }

            byte[] tmp = new byte[0x400];
            Array.Copy(vhSector, 0x400, tmp, 0, 0x400);
            vhSector = tmp;

            vh = Marshal.ByteArrayToStructureBigEndian <VolumeHeader>(vhSector);

            if (vh.version == 4 ||
                vh.version == 5)
            {
                sb.AppendFormat("Filesystem version is {0}.", vh.version).AppendLine();

                if ((vh.attributes & 0x80) == 0x80)
                {
                    sb.AppendLine("Volume is locked on hardware.");
                }

                if ((vh.attributes & 0x100) == 0x100)
                {
                    sb.AppendLine("Volume is unmounted.");
                }

                if ((vh.attributes & 0x200) == 0x200)
                {
                    sb.AppendLine("There are bad blocks in the extents file.");
                }

                if ((vh.attributes & 0x400) == 0x400)
                {
                    sb.AppendLine("Volume does not require cache.");
                }

                if ((vh.attributes & 0x800) == 0x800)
                {
                    sb.AppendLine("Volume state is inconsistent.");
                }

                if ((vh.attributes & 0x1000) == 0x1000)
                {
                    sb.AppendLine("CNIDs are reused.");
                }

                if ((vh.attributes & 0x2000) == 0x2000)
                {
                    sb.AppendLine("Volume is journaled.");
                }

                if ((vh.attributes & 0x8000) == 0x8000)
                {
                    sb.AppendLine("Volume is locked on software.");
                }

                sb.AppendFormat("Implementation that last mounted the volume: \"{0}\".",
                                Encoding.ASCII.GetString(vh.lastMountedVersion)).AppendLine();

                if ((vh.attributes & 0x2000) == 0x2000)
                {
                    sb.AppendFormat("Journal starts at allocation block {0}.", vh.journalInfoBlock).AppendLine();
                }

                sb.AppendFormat("Creation date: {0}", DateHandlers.MacToDateTime(vh.createDate)).AppendLine();
                sb.AppendFormat("Last modification date: {0}", DateHandlers.MacToDateTime(vh.modifyDate)).AppendLine();

                if (vh.backupDate > 0)
                {
                    sb.AppendFormat("Last backup date: {0}", DateHandlers.MacToDateTime(vh.backupDate)).AppendLine();
                }
                else
                {
                    sb.AppendLine("Volume has never been backed up");
                }

                if (vh.backupDate > 0)
                {
                    sb.AppendFormat("Last check date: {0}", DateHandlers.MacToDateTime(vh.checkedDate)).AppendLine();
                }
                else
                {
                    sb.AppendLine("Volume has never been checked up");
                }

                sb.AppendFormat("{0} files on volume.", vh.fileCount).AppendLine();
                sb.AppendFormat("{0} folders on volume.", vh.folderCount).AppendLine();
                sb.AppendFormat("{0} bytes per allocation block.", vh.blockSize).AppendLine();
                sb.AppendFormat("{0} allocation blocks.", vh.totalBlocks).AppendLine();
                sb.AppendFormat("{0} free blocks.", vh.freeBlocks).AppendLine();
                sb.AppendFormat("Next allocation block: {0}.", vh.nextAllocation).AppendLine();
                sb.AppendFormat("Resource fork clump size: {0} bytes.", vh.rsrcClumpSize).AppendLine();
                sb.AppendFormat("Data fork clump size: {0} bytes.", vh.dataClumpSize).AppendLine();
                sb.AppendFormat("Next unused CNID: {0}.", vh.nextCatalogID).AppendLine();
                sb.AppendFormat("Volume has been mounted writable {0} times.", vh.writeCount).AppendLine();
                sb.AppendFormat("Allocation File is {0} bytes.", vh.allocationFile_logicalSize).AppendLine();
                sb.AppendFormat("Extents File is {0} bytes.", vh.extentsFile_logicalSize).AppendLine();
                sb.AppendFormat("Catalog File is {0} bytes.", vh.catalogFile_logicalSize).AppendLine();
                sb.AppendFormat("Attributes File is {0} bytes.", vh.attributesFile_logicalSize).AppendLine();
                sb.AppendFormat("Startup File is {0} bytes.", vh.startupFile_logicalSize).AppendLine();
                sb.AppendLine("Finder info:");
                sb.AppendFormat("CNID of bootable system's directory: {0}", vh.drFndrInfo0).AppendLine();
                sb.AppendFormat("CNID of first-run application's directory: {0}", vh.drFndrInfo1).AppendLine();
                sb.AppendFormat("CNID of previously opened directory: {0}", vh.drFndrInfo2).AppendLine();
                sb.AppendFormat("CNID of bootable Mac OS 8 or 9 directory: {0}", vh.drFndrInfo3).AppendLine();
                sb.AppendFormat("CNID of bootable Mac OS X directory: {0}", vh.drFndrInfo5).AppendLine();

                if (vh.drFndrInfo6 != 0 &&
                    vh.drFndrInfo7 != 0)
                {
                    sb.AppendFormat("Mac OS X Volume ID: {0:X8}{1:X8}", vh.drFndrInfo6, vh.drFndrInfo7).AppendLine();
                }

                XmlFsType = new FileSystemType();

                if (vh.backupDate > 0)
                {
                    XmlFsType.BackupDate          = DateHandlers.MacToDateTime(vh.backupDate);
                    XmlFsType.BackupDateSpecified = true;
                }

                XmlFsType.Bootable   |= vh.drFndrInfo0 != 0 || vh.drFndrInfo3 != 0 || vh.drFndrInfo5 != 0;
                XmlFsType.Clusters    = vh.totalBlocks;
                XmlFsType.ClusterSize = vh.blockSize;

                if (vh.createDate > 0)
                {
                    XmlFsType.CreationDate          = DateHandlers.MacToDateTime(vh.createDate);
                    XmlFsType.CreationDateSpecified = true;
                }

                XmlFsType.Dirty                 = (vh.attributes & 0x100) != 0x100;
                XmlFsType.Files                 = vh.fileCount;
                XmlFsType.FilesSpecified        = true;
                XmlFsType.FreeClusters          = vh.freeBlocks;
                XmlFsType.FreeClustersSpecified = true;

                if (vh.modifyDate > 0)
                {
                    XmlFsType.ModificationDate          = DateHandlers.MacToDateTime(vh.modifyDate);
                    XmlFsType.ModificationDateSpecified = true;
                }

                if (vh.signature == 0x482B)
                {
                    XmlFsType.Type = "HFS+";
                }

                if (vh.signature == 0x4858)
                {
                    XmlFsType.Type = "HFSX";
                }

                if (vh.drFndrInfo6 != 0 &&
                    vh.drFndrInfo7 != 0)
                {
                    XmlFsType.VolumeSerial = $"{vh.drFndrInfo6:X8}{vh.drFndrInfo7:X8}";
                }

                XmlFsType.SystemIdentifier = Encoding.ASCII.GetString(vh.lastMountedVersion);
            }
            else
            {
                sb.AppendFormat("Filesystem version is {0}.", vh.version).AppendLine();
                sb.AppendLine("This version is not supported yet.");
            }

            information = sb.ToString();
        }
Пример #29
0
        public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information,
                                   Encoding encoding)
        {
            Encoding    = Encoding.Unicode;
            information = "";

            if (imagePlugin.Info.SectorSize < F2FS_MIN_SECTOR ||
                imagePlugin.Info.SectorSize > F2FS_MAX_SECTOR)
            {
                return;
            }

            uint sbAddr = F2FS_SUPER_OFFSET / imagePlugin.Info.SectorSize;

            if (sbAddr == 0)
            {
                sbAddr = 1;
            }

            uint sbSize = (uint)(Marshal.SizeOf <Superblock>() / imagePlugin.Info.SectorSize);

            if (Marshal.SizeOf <Superblock>() % imagePlugin.Info.SectorSize != 0)
            {
                sbSize++;
            }

            byte[] sector = imagePlugin.ReadSectors(partition.Start + sbAddr, sbSize);

            if (sector.Length < Marshal.SizeOf <Superblock>())
            {
                return;
            }

            Superblock f2fsSb = Marshal.ByteArrayToStructureLittleEndian <Superblock>(sector);

            if (f2fsSb.magic != F2FS_MAGIC)
            {
                return;
            }

            var sb = new StringBuilder();

            sb.AppendLine("F2FS filesystem");
            sb.AppendFormat("Version {0}.{1}", f2fsSb.major_ver, f2fsSb.minor_ver).AppendLine();
            sb.AppendFormat("{0} bytes per sector", 1 << (int)f2fsSb.log_sectorsize).AppendLine();

            sb.AppendFormat("{0} sectors ({1} bytes) per block", 1 << (int)f2fsSb.log_sectors_per_block,
                            1 << (int)f2fsSb.log_blocksize).AppendLine();

            sb.AppendFormat("{0} blocks per segment", f2fsSb.log_blocks_per_seg).AppendLine();
            sb.AppendFormat("{0} blocks in volume", f2fsSb.block_count).AppendLine();
            sb.AppendFormat("{0} segments per section", f2fsSb.segs_per_sec).AppendLine();
            sb.AppendFormat("{0} sections per zone", f2fsSb.secs_per_zone).AppendLine();
            sb.AppendFormat("{0} sections", f2fsSb.section_count).AppendLine();
            sb.AppendFormat("{0} segments", f2fsSb.segment_count).AppendLine();
            sb.AppendFormat("Root directory resides on inode {0}", f2fsSb.root_ino).AppendLine();
            sb.AppendFormat("Volume UUID: {0}", f2fsSb.uuid).AppendLine();

            sb.AppendFormat("Volume name: {0}", StringHandlers.CToString(f2fsSb.volume_name, Encoding.Unicode, true)).
            AppendLine();

            sb.AppendFormat("Volume last mounted on kernel version: {0}", StringHandlers.CToString(f2fsSb.version)).
            AppendLine();

            sb.AppendFormat("Volume created on kernel version: {0}", StringHandlers.CToString(f2fsSb.init_version)).
            AppendLine();

            information = sb.ToString();

            XmlFsType = new FileSystemType
            {
                Type                   = "F2FS filesystem",
                SystemIdentifier       = Encoding.ASCII.GetString(f2fsSb.version),
                Clusters               = f2fsSb.block_count,
                ClusterSize            = (uint)(1 << (int)f2fsSb.log_blocksize),
                DataPreparerIdentifier = Encoding.ASCII.GetString(f2fsSb.init_version),
                VolumeName             = StringHandlers.CToString(f2fsSb.volume_name, Encoding.Unicode, true),
                VolumeSerial           = f2fsSb.uuid.ToString()
            };
        }
Пример #30
0
        public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information,
                                   Encoding encoding)
        {
            Encoding    = encoding ?? Encoding.GetEncoding("iso-8859-15");
            information = "";
            if (imagePlugin.Info.SectorSize < 512)
            {
                return;
            }

            Locus_Superblock LocusSb = new Locus_Superblock();

            byte[] sector = null;

            for (ulong location = 0; location <= 8; location++)
            {
                uint sbSize = (uint)(Marshal.SizeOf(LocusSb) / imagePlugin.Info.SectorSize);
                if (Marshal.SizeOf(LocusSb) % imagePlugin.Info.SectorSize != 0)
                {
                    sbSize++;
                }

                sector = imagePlugin.ReadSectors(partition.Start + location, sbSize);
                if (sector.Length < Marshal.SizeOf(LocusSb))
                {
                    return;
                }

                IntPtr sbPtr = Marshal.AllocHGlobal(Marshal.SizeOf(LocusSb));
                Marshal.Copy(sector, 0, sbPtr, Marshal.SizeOf(LocusSb));
                LocusSb = (Locus_Superblock)Marshal.PtrToStructure(sbPtr, typeof(Locus_Superblock));
                Marshal.FreeHGlobal(sbPtr);

                if (LocusSb.s_magic == Locus_Magic || LocusSb.s_magic == Locus_Cigam ||
                    LocusSb.s_magic == Locus_OldMagic || LocusSb.s_magic == Locus_OldCigam)
                {
                    break;
                }
            }

            // We don't care about old version for information
            if (LocusSb.s_magic != Locus_Magic && LocusSb.s_magic != Locus_Cigam && LocusSb.s_magic != Locus_OldMagic &&
                LocusSb.s_magic != Locus_OldCigam)
            {
                return;
            }

            // Numerical arrays are not important for information so no need to swap them
            if (LocusSb.s_magic == Locus_Cigam || LocusSb.s_magic == Locus_OldCigam)
            {
                LocusSb         = BigEndianMarshal.ByteArrayToStructureBigEndian <Locus_Superblock>(sector);
                LocusSb.s_flags = (LocusFlags)Swapping.Swap((ushort)LocusSb.s_flags);
            }

            StringBuilder sb = new StringBuilder();

            sb.AppendLine(LocusSb.s_magic == Locus_OldMagic ? "Locus filesystem (old)" : "Locus filesystem");

            int blockSize = LocusSb.s_version == LocusVersion.SB_SB4096 ? 4096 : 1024;

            string s_fsmnt = StringHandlers.CToString(LocusSb.s_fsmnt, Encoding);
            string s_fpack = StringHandlers.CToString(LocusSb.s_fpack, Encoding);

            DicConsole.DebugWriteLine("Locus plugin", "LocusSb.s_magic = 0x{0:X8}", LocusSb.s_magic);
            DicConsole.DebugWriteLine("Locus plugin", "LocusSb.s_gfs = {0}", LocusSb.s_gfs);
            DicConsole.DebugWriteLine("Locus plugin", "LocusSb.s_fsize = {0}", LocusSb.s_fsize);
            DicConsole.DebugWriteLine("Locus plugin", "LocusSb.s_lwm = {0}", LocusSb.s_lwm);
            DicConsole.DebugWriteLine("Locus plugin", "LocusSb.s_hwm = {0}", LocusSb.s_hwm);
            DicConsole.DebugWriteLine("Locus plugin", "LocusSb.s_llst = {0}", LocusSb.s_llst);
            DicConsole.DebugWriteLine("Locus plugin", "LocusSb.s_fstore = {0}", LocusSb.s_fstore);
            DicConsole.DebugWriteLine("Locus plugin", "LocusSb.s_time = {0}", LocusSb.s_time);
            DicConsole.DebugWriteLine("Locus plugin", "LocusSb.s_tfree = {0}", LocusSb.s_tfree);
            DicConsole.DebugWriteLine("Locus plugin", "LocusSb.s_isize = {0}", LocusSb.s_isize);
            DicConsole.DebugWriteLine("Locus plugin", "LocusSb.s_nfree = {0}", LocusSb.s_nfree);
            DicConsole.DebugWriteLine("Locus plugin", "LocusSb.s_flags = {0}", LocusSb.s_flags);
            DicConsole.DebugWriteLine("Locus plugin", "LocusSb.s_tinode = {0}", LocusSb.s_tinode);
            DicConsole.DebugWriteLine("Locus plugin", "LocusSb.s_lasti = {0}", LocusSb.s_lasti);
            DicConsole.DebugWriteLine("Locus plugin", "LocusSb.s_nbehind = {0}", LocusSb.s_nbehind);
            DicConsole.DebugWriteLine("Locus plugin", "LocusSb.s_gfspack = {0}", LocusSb.s_gfspack);
            DicConsole.DebugWriteLine("Locus plugin", "LocusSb.s_ninode = {0}", LocusSb.s_ninode);
            DicConsole.DebugWriteLine("Locus plugin", "LocusSb.s_flock = {0}", LocusSb.s_flock);
            DicConsole.DebugWriteLine("Locus plugin", "LocusSb.s_ilock = {0}", LocusSb.s_ilock);
            DicConsole.DebugWriteLine("Locus plugin", "LocusSb.s_fmod = {0}", LocusSb.s_fmod);
            DicConsole.DebugWriteLine("Locus plugin", "LocusSb.s_version = {0}", LocusSb.s_version);

            sb.AppendFormat("Superblock last modified on {0}", DateHandlers.UnixToDateTime(LocusSb.s_time))
            .AppendLine();
            sb.AppendFormat("Volume has {0} blocks of {1} bytes each (total {2} bytes)", LocusSb.s_fsize, blockSize,
                            LocusSb.s_fsize * blockSize).AppendLine();
            sb.AppendFormat("{0} blocks free ({1} bytes)", LocusSb.s_tfree, LocusSb.s_tfree * blockSize).AppendLine();
            sb.AppendFormat("I-node list uses {0} blocks", LocusSb.s_isize).AppendLine();
            sb.AppendFormat("{0} free inodes", LocusSb.s_tinode).AppendLine();
            sb.AppendFormat("Next free inode search will start at inode {0}", LocusSb.s_lasti).AppendLine();
            sb.AppendFormat("There are an estimate of {0} free inodes before next search start", LocusSb.s_nbehind)
            .AppendLine();
            if (LocusSb.s_flags.HasFlag(LocusFlags.SB_RDONLY))
            {
                sb.AppendLine("Read-only volume");
            }
            if (LocusSb.s_flags.HasFlag(LocusFlags.SB_CLEAN))
            {
                sb.AppendLine("Clean volume");
            }
            if (LocusSb.s_flags.HasFlag(LocusFlags.SB_DIRTY))
            {
                sb.AppendLine("Dirty volume");
            }
            if (LocusSb.s_flags.HasFlag(LocusFlags.SB_RMV))
            {
                sb.AppendLine("Removable volume");
            }
            if (LocusSb.s_flags.HasFlag(LocusFlags.SB_PRIMPACK))
            {
                sb.AppendLine("This is the primary pack");
            }
            if (LocusSb.s_flags.HasFlag(LocusFlags.SB_REPLTYPE))
            {
                sb.AppendLine("Replicated volume");
            }
            if (LocusSb.s_flags.HasFlag(LocusFlags.SB_USER))
            {
                sb.AppendLine("User replicated volume");
            }
            if (LocusSb.s_flags.HasFlag(LocusFlags.SB_BACKBONE))
            {
                sb.AppendLine("Backbone volume");
            }
            if (LocusSb.s_flags.HasFlag(LocusFlags.SB_NFS))
            {
                sb.AppendLine("NFS volume");
            }
            if (LocusSb.s_flags.HasFlag(LocusFlags.SB_BYHAND))
            {
                sb.AppendLine("Volume inhibits automatic fsck");
            }
            if (LocusSb.s_flags.HasFlag(LocusFlags.SB_NOSUID))
            {
                sb.AppendLine("Set-uid/set-gid is disabled");
            }
            if (LocusSb.s_flags.HasFlag(LocusFlags.SB_SYNCW))
            {
                sb.AppendLine("Volume uses synchronous writes");
            }
            sb.AppendFormat("Volume label: {0}", s_fsmnt).AppendLine();
            sb.AppendFormat("Physical volume name: {0}", s_fpack).AppendLine();
            sb.AppendFormat("Global File System number: {0}", LocusSb.s_gfs).AppendLine();
            sb.AppendFormat("Global File System pack number {0}", LocusSb.s_gfspack).AppendLine();

            information = sb.ToString();

            XmlFsType = new FileSystemType
            {
                Type        = "Locus filesystem",
                ClusterSize = blockSize,
                Clusters    = LocusSb.s_fsize,
                // Sometimes it uses one, or the other. Use the bigger
                VolumeName                = string.IsNullOrEmpty(s_fsmnt) ? s_fpack : s_fsmnt,
                ModificationDate          = DateHandlers.UnixToDateTime(LocusSb.s_time),
                ModificationDateSpecified = true,
                Dirty                 = !LocusSb.s_flags.HasFlag(LocusFlags.SB_CLEAN) || LocusSb.s_flags.HasFlag(LocusFlags.SB_DIRTY),
                FreeClusters          = LocusSb.s_tfree,
                FreeClustersSpecified = true
            };
        }