diff --git a/.idea/.idea.DiscImageChef/.idea/contentModel.xml b/.idea/.idea.DiscImageChef/.idea/contentModel.xml index ce3654174..34a1ebf7e 100644 --- a/.idea/.idea.DiscImageChef/.idea/contentModel.xml +++ b/.idea/.idea.DiscImageChef/.idea/contentModel.xml @@ -594,6 +594,7 @@ + @@ -843,6 +844,7 @@ + diff --git a/DiscImageChef.Filesystems/DiscImageChef.Filesystems.csproj b/DiscImageChef.Filesystems/DiscImageChef.Filesystems.csproj index 84adab4e5..355ae0a48 100644 --- a/DiscImageChef.Filesystems/DiscImageChef.Filesystems.csproj +++ b/DiscImageChef.Filesystems/DiscImageChef.Filesystems.csproj @@ -1,4 +1,4 @@ - + Debug @@ -116,6 +116,7 @@ + diff --git a/DiscImageChef.Filesystems/Xia.cs b/DiscImageChef.Filesystems/Xia.cs new file mode 100644 index 000000000..842214598 --- /dev/null +++ b/DiscImageChef.Filesystems/Xia.cs @@ -0,0 +1,266 @@ +// /*************************************************************************** +// The Disc Image Chef +// ---------------------------------------------------------------------------- +// +// Filename : Xia.cs +// Author(s) : Natalia Portillo +// +// Component : Xia filesystem plugin. +// +// --[ Description ] ---------------------------------------------------------- +// +// Identifies the Xia filesystem and shows information. +// +// --[ License ] -------------------------------------------------------------- +// +// This library is free software; you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation; either version 2.1 of the +// License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, see . +// +// ---------------------------------------------------------------------------- +// Copyright © 2011-2018 Natalia Portillo +// ****************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Text; +using DiscImageChef.CommonTypes; +using DiscImageChef.DiscImages; +using Schemas; + +namespace DiscImageChef.Filesystems +{ + // Information from the Linux kernel + public class Xia : Filesystem + { + const uint XIAFS_SUPER_MAGIC = 0x012FD16D; + const uint XIAFS_ROOT_INO = 1; + const uint XIAFS_BAD_INO = 2; + const int XIAFS_MAX_LINK = 64000; + const int XIAFS_DIR_SIZE = 12; + const int XIAFS_NUM_BLOCK_POINTERS = 10; + const int XIAFS_NAME_LEN = 248; + + public Xia() + { + Name = "Xia filesystem"; + PluginUuid = new Guid("169E1DE5-24F2-4EF6-A04D-A4B2CA66DE9D"); + CurrentEncoding = Encoding.GetEncoding("iso-8859-15"); + } + + public Xia(Encoding encoding) + { + Name = "Xia filesystem"; + PluginUuid = new Guid("169E1DE5-24F2-4EF6-A04D-A4B2CA66DE9D"); + CurrentEncoding = encoding ?? Encoding.GetEncoding("iso-8859-15"); + } + + public Xia(ImagePlugin imagePlugin, Partition partition, Encoding encoding) + { + Name = "Xia filesystem"; + PluginUuid = new Guid("169E1DE5-24F2-4EF6-A04D-A4B2CA66DE9D"); + CurrentEncoding = encoding ?? Encoding.GetEncoding("iso-8859-15"); + } + + public override bool Identify(ImagePlugin imagePlugin, Partition partition) + { + int sbSizeInBytes = Marshal.SizeOf(typeof(XiaSuperBlock)); + uint sbSizeInSectors = (uint)(sbSizeInBytes / imagePlugin.GetSectorSize()); + if(sbSizeInBytes % imagePlugin.GetSectorSize() > 0) sbSizeInSectors++; + if(sbSizeInSectors + partition.Start >= partition.End) return false; + + byte[] sbSector = imagePlugin.ReadSectors(partition.Start, sbSizeInSectors); + IntPtr sbPtr = Marshal.AllocHGlobal(sbSizeInBytes); + Marshal.Copy(sbSector, 0, sbPtr, sbSizeInBytes); + XiaSuperBlock supblk = (XiaSuperBlock)Marshal.PtrToStructure(sbPtr, typeof(XiaSuperBlock)); + Marshal.FreeHGlobal(sbPtr); + + return supblk.s_magic == XIAFS_SUPER_MAGIC; + } + + public override void GetInformation(ImagePlugin imagePlugin, Partition partition, out string information) + { + information = ""; + + StringBuilder sb = new StringBuilder(); + + int sbSizeInBytes = Marshal.SizeOf(typeof(XiaSuperBlock)); + uint sbSizeInSectors = (uint)(sbSizeInBytes / imagePlugin.GetSectorSize()); + if(sbSizeInBytes % imagePlugin.GetSectorSize() > 0) sbSizeInSectors++; + + byte[] sbSector = imagePlugin.ReadSectors(partition.Start, sbSizeInSectors); + IntPtr sbPtr = Marshal.AllocHGlobal(sbSizeInBytes); + Marshal.Copy(sbSector, 0, sbPtr, sbSizeInBytes); + XiaSuperBlock supblk = (XiaSuperBlock)Marshal.PtrToStructure(sbPtr, typeof(XiaSuperBlock)); + Marshal.FreeHGlobal(sbPtr); + + sb.AppendFormat("{0} bytes per zone", supblk.s_zone_size).AppendLine(); + sb.AppendFormat("{0} zones in volume ({1} bytes)", supblk.s_nzones, supblk.s_nzones * supblk.s_zone_size) + .AppendLine(); + sb.AppendFormat("{0} inodes", supblk.s_ninodes).AppendLine(); + sb.AppendFormat("{0} data zones ({1} bytes)", supblk.s_ndatazones, supblk.s_ndatazones * supblk.s_zone_size) + .AppendLine(); + sb.AppendFormat("{0} imap zones ({1} bytes)", supblk.s_imap_zones, supblk.s_imap_zones * supblk.s_zone_size) + .AppendLine(); + sb.AppendFormat("{0} zmap zones ({1} bytes)", supblk.s_zmap_zones, supblk.s_zmap_zones * supblk.s_zone_size) + .AppendLine(); + sb.AppendFormat("First data zone: {0}", supblk.s_firstdatazone).AppendLine(); + sb.AppendFormat("Maximum filesize is {0} bytes ({1} MiB)", supblk.s_max_size, supblk.s_max_size / 1048576) + .AppendLine(); + sb.AppendFormat("{0} zones reserved for kernel images ({1} bytes)", supblk.s_kernzones, + supblk.s_kernzones * supblk.s_zone_size).AppendLine(); + sb.AppendFormat("First kernel zone: {0}", supblk.s_firstkernzone).AppendLine(); + + XmlFsType = new FileSystemType + { + Bootable = !ArrayHelpers.ArrayIsNullOrEmpty(supblk.s_boot_segment), + Clusters = supblk.s_nzones, + ClusterSize = (int)supblk.s_zone_size, + Type = "Xia filesystem" + }; + + information = sb.ToString(); + } + + public override Errno Mount() + { + return Errno.NotImplemented; + } + + public override Errno Mount(bool debug) + { + return Errno.NotImplemented; + } + + public override Errno Unmount() + { + return Errno.NotImplemented; + } + + public override Errno MapBlock(string path, long fileBlock, ref long deviceBlock) + { + return Errno.NotImplemented; + } + + public override Errno GetAttributes(string path, ref FileAttributes attributes) + { + return Errno.NotImplemented; + } + + public override Errno ListXAttr(string path, ref List xattrs) + { + return Errno.NotImplemented; + } + + public override Errno GetXattr(string path, string xattr, ref byte[] buf) + { + return Errno.NotImplemented; + } + + public override Errno Read(string path, long offset, long size, ref byte[] buf) + { + return Errno.NotImplemented; + } + + public override Errno ReadDir(string path, ref List contents) + { + return Errno.NotImplemented; + } + + public override Errno StatFs(ref FileSystemInfo stat) + { + return Errno.NotImplemented; + } + + public override Errno Stat(string path, ref FileEntryInfo stat) + { + return Errno.NotImplemented; + } + + public override Errno ReadLink(string path, ref string dest) + { + return Errno.NotImplemented; + } + + /// + /// Xia superblock + /// + [StructLayout(LayoutKind.Sequential, Pack = 1)] + struct XiaSuperBlock + { + /// 1st sector reserved for boot + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 512)] public byte[] s_boot_segment; + /// the name says it + public uint s_zone_size; + /// volume size, zone aligned + public uint s_nzones; + /// # of inodes + public uint s_ninodes; + /// # of data zones + public uint s_ndatazones; + /// # of imap zones + public uint s_imap_zones; + /// # of zmap zones + public uint s_zmap_zones; + /// first data zone + public uint s_firstdatazone; + /// z size = 1KB << z shift + public uint s_zone_shift; + /// max size of a single file + public uint s_max_size; + /// reserved + public uint s_reserved0; + /// reserved + public uint s_reserved1; + /// reserved + public uint s_reserved2; + /// reserved + public uint s_reserved3; + /// first kernel zone + public uint s_firstkernzone; + /// kernel size in zones + public uint s_kernzones; + /// magic number for xiafs + public uint s_magic; + } + + /// + /// Xia directory entry + /// + [StructLayout(LayoutKind.Sequential, Pack = 1)] + struct XiaDirect + { + public uint d_ino; + public ushort d_rec_len; + public byte d_name_len; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = XIAFS_NAME_LEN + 1)] public byte[] d_name; + } + + /// + /// Xia inode + /// + [StructLayout(LayoutKind.Sequential, Pack = 1)] + struct XiaInode + { + public ushort i_mode; + public ushort i_nlinks; + public ushort i_uid; + public ushort i_gid; + public uint i_size; + public uint i_ctime; + public uint i_atime; + public uint i_mtime; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = XIAFS_NUM_BLOCK_POINTERS)] public uint[] i_zone; + } + } +} \ No newline at end of file diff --git a/DiscImageChef.Tests/DiscImageChef.Tests.csproj b/DiscImageChef.Tests/DiscImageChef.Tests.csproj index e3a5f585a..f6c90c925 100644 --- a/DiscImageChef.Tests/DiscImageChef.Tests.csproj +++ b/DiscImageChef.Tests/DiscImageChef.Tests.csproj @@ -52,6 +52,7 @@ + diff --git a/DiscImageChef.Tests/Filesystems/Xia.cs b/DiscImageChef.Tests/Filesystems/Xia.cs new file mode 100644 index 000000000..fbfd06283 --- /dev/null +++ b/DiscImageChef.Tests/Filesystems/Xia.cs @@ -0,0 +1,89 @@ +// /*************************************************************************** +// The Disc Image Chef +// ---------------------------------------------------------------------------- +// +// Filename : Xia.cs +// Author(s) : Natalia Portillo +// +// Component : DiscImageChef unit testing. +// +// --[ License ] -------------------------------------------------------------- +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// ---------------------------------------------------------------------------- +// Copyright © 2011-2018 Natalia Portillo +// ****************************************************************************/ + +using System.Collections.Generic; +using System.IO; +using DiscImageChef.CommonTypes; +using DiscImageChef.DiscImages; +using DiscImageChef.Filesystems; +using DiscImageChef.Filters; +using NUnit.Framework; + +namespace DiscImageChef.Tests.Filesystems +{ + [TestFixture] + public class XiaMbr + { + readonly string[] testfiles = {"linux.vdi.lz"}; + + readonly ulong[] sectors = {1024000}; + + readonly uint[] sectorsize = {512}; + + readonly long[] clusters = {511528}; + + readonly int[] clustersize = {1024}; + + readonly string[] volumename = {null}; + + readonly string[] volumeserial = {null}; + + [Test] + public void Test() + { + for(int i = 0; i < testfiles.Length; i++) + { + string location = Path.Combine(Consts.TestFilesRoot, "filesystems", "xia_mbr", testfiles[i]); + Filter filter = new LZip(); + filter.Open(location); + ImagePlugin image = new Vdi(); + Assert.AreEqual(true, image.OpenImage(filter), testfiles[i]); + Assert.AreEqual(sectors[i], image.ImageInfo.Sectors, testfiles[i]); + Assert.AreEqual(sectorsize[i], image.ImageInfo.SectorSize, testfiles[i]); + List partitions = Core.Partitions.GetAll(image); + Filesystem fs = new Xia(); + int part = -1; + for(int j = 0; j < partitions.Count; j++) + if(partitions[j].Type == "0x83") + { + part = j; + break; + } + + Assert.AreNotEqual(-1, part, $"Partition not found on {testfiles[i]}"); + Assert.AreEqual(true, fs.Identify(image, partitions[part]), testfiles[i]); + fs.GetInformation(image, partitions[part], out _); + Assert.AreEqual(clusters[i], fs.XmlFSType.Clusters, testfiles[i]); + Assert.AreEqual(clustersize[i], fs.XmlFSType.ClusterSize, testfiles[i]); + Assert.AreEqual("Xia filesystem", fs.XmlFSType.Type, testfiles[i]); + Assert.AreEqual(volumename[i], fs.XmlFSType.VolumeName, testfiles[i]); + Assert.AreEqual(volumeserial[i], fs.XmlFSType.VolumeSerial, testfiles[i]); + } + } + } +} \ No newline at end of file diff --git a/README.md b/README.md index 661120255..cbc15f1b3 100644 --- a/README.md +++ b/README.md @@ -201,6 +201,7 @@ Supported file systems for identification and information only * Veritas file system * Xbox filesystems * Xenix file system +* Xia filesystem * Zettabyte File System (ZFS) Supported checksums diff --git a/TODO.md b/TODO.md index 40b480bab..780788c2b 100644 --- a/TODO.md +++ b/TODO.md @@ -60,7 +60,6 @@ * Add support for SecureDigital and MultiMediaCard devices in FreeBSD. (https://github.com/claunia/DiscImageChef/issues/116) * Add support for Sharp X68000 FAT filesystem variation. (https://github.com/claunia/DiscImageChef/issues/158) * Add support for SuperCardPro devices. (https://github.com/claunia/DiscImageChef/issues/139) -* Add support for Xia filesystem. (https://github.com/claunia/DiscImageChef/issues/152) * Add support for XPACK images. (https://github.com/claunia/DiscImageChef/issues/45) * Check CompactDisc read capabilities on dumping. (https://github.com/claunia/DiscImageChef/issues/138) * Checksum disk tags