diff --git a/DiscImageChef.Filesystems/DiscImageChef.Filesystems.csproj b/DiscImageChef.Filesystems/DiscImageChef.Filesystems.csproj index a8d488841..096338e06 100644 --- a/DiscImageChef.Filesystems/DiscImageChef.Filesystems.csproj +++ b/DiscImageChef.Filesystems/DiscImageChef.Filesystems.csproj @@ -135,6 +135,7 @@ + @@ -192,7 +193,7 @@ - + diff --git a/DiscImageChef.Filesystems/dump.cs b/DiscImageChef.Filesystems/dump.cs new file mode 100644 index 000000000..c368a0234 --- /dev/null +++ b/DiscImageChef.Filesystems/dump.cs @@ -0,0 +1,502 @@ +// /*************************************************************************** +// The Disc Image Chef +// ---------------------------------------------------------------------------- +// +// Filename : dump.cs +// Author(s) : Natalia Portillo +// +// Component : Component +// +// --[ Description ] ---------------------------------------------------------- +// +// Description +// +// --[ 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-2017 Natalia Portillo +// ****************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Text; +using DiscImageChef.CommonTypes; +using DiscImageChef.Console; +using ufs_daddr_t = System.Int32; + +namespace DiscImageChef.Filesystems +{ + public class dump : Filesystem + { + /// Magic number for old dump + const ushort OFS_MAGIC = 60011; + /// Magic number for new dump + const uint NFS_MAGIC = 60012; + /// Magic number for AIX dump + const uint XIX_MAGIC = 60013; + /// Magic number for UFS2 dump + const uint UFS2_MAGIC = 0x19540119; + /// Magic number for old dump + const uint OFS_CIGAM = 0x6BEA0000; + /// Magic number for new dump + const uint NFS_CIGAM = 0x6CEA0000; + /// Magic number for AIX dump + const uint XIX_CIGAM = 0x6DEA0000; + /// Magic number for UFS2 dump + const uint UFS2_CIGAM = 0x19015419; + + const int TP_BSIZE = 1024; + + /// + /// Dump tape header + /// + const short TS_TAPE = 1; + /// + /// Beginning of file record + /// + const short TS_INODE = 2; + /// + /// Map of inodes on tape + /// + const short TS_BITS = 3; + /// + /// Continuation of file record + /// + const short TS_ADDR = 4; + /// + /// Map of inodes deleted since last dump + /// + const short TS_END = 5; + /// + /// Inode bitmap + /// + const short TS_CLRI = 6; + const short TS_ACL = 7; + const short TS_PCL = 8; + + // Old 16-bit format record + [StructLayout(LayoutKind.Sequential, Pack = 1)] + struct spcl16 + { + /// Record type + public short c_type; + /// Dump date + public int c_date; + /// Previous dump date + public int c_ddate; + /// Dump volume number + public short c_volume; + /// Logical block of this record + public int c_tapea; + /// Inode number + public ushort c_inumber; + /// Magic number + public ushort c_magic; + /// Record checksum + public int c_checksum; + // Unneeded for now + /* + struct dinode c_dinode; + int c_count; + char c_addr[BSIZE]; + */ + } + + // 32-bit AIX format record + [StructLayout(LayoutKind.Sequential, Pack = 1)] + struct spcl_aix + { + /// Record type + public int c_type; + /// Dump date + public int c_date; + /// Previous dump date + public int c_ddate; + /// Dump volume number + public int c_volume; + /// Logical block of this record + public int c_tapea; + public uint c_inumber; + public uint c_magic; + public int c_checksum; + // Unneeded for now + /* + public bsd_dinode bsd_c_dinode; + public int c_count; + public char c_addr[TP_NINDIR]; + public int xix_flag; + public dinode xix_dinode; + */ + } + + const int TP_NINDIR = TP_BSIZE / 2; + const int LBLSIZE = 16; + const int NAMELEN = 64; + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + struct s_spcl + { + public int c_type; /* record type (see below) */ + public int c_date; /* date of this dump */ + public int c_ddate; /* date of previous dump */ + public int c_volume; /* dump volume number */ + public int c_tapea; /* logical block of this record */ + public uint c_inumber; /* number of inode */ + public int c_magic; /* magic number (see above) */ + public int c_checksum; /* record checksum */ + public dinode c_dinode; /* ownership and mode of inode */ + public int c_count; /* number of valid c_addr entries */ + [MarshalAs(UnmanagedType.ByValArray, SizeConst = TP_NINDIR)] + public byte[] c_addr; /* 1 => data; 0 => hole in inode */ + [MarshalAs(UnmanagedType.ByValArray, SizeConst = LBLSIZE)] + public byte[] c_label; /* dump label */ + public int c_level; /* level of this dump */ + [MarshalAs(UnmanagedType.ByValArray, SizeConst = NAMELEN)] + public byte[] c_filesys; /* name of dumpped file system */ + [MarshalAs(UnmanagedType.ByValArray, SizeConst = NAMELEN)] + public byte[] c_dev; /* name of dumpped device */ + [MarshalAs(UnmanagedType.ByValArray, SizeConst = NAMELEN)] + public byte[] c_host; /* name of dumpped host */ + public int c_flags; /* additional information */ + public int c_firstrec; /* first record on volume */ + public long c_ndate; /* date of this dump */ + public long c_nddate; /* date of previous dump */ + public long c_ntapea; /* logical block of this record */ + public long c_nfirstrec; /* first record on volume */ + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] + public int[] c_spare; /* reserved for future uses */ + } + + const int NDADDR = 12; + const int NIADDR = 3; + + struct dinode + { + public ushort di_mode; /* 0: IFMT, permissions; see below. */ + public short di_nlink; /* 2: File link count. */ + public int inumber; /* 4: Lfs: inode number. */ + public ulong di_size; /* 8: File byte count. */ + public int di_atime; /* 16: Last access time. */ + public int di_atimensec; /* 20: Last access time. */ + public int di_mtime; /* 24: Last modified time. */ + public int di_mtimensec; /* 28: Last modified time. */ + public int di_ctime; /* 32: Last inode change time. */ + public int di_ctimensec; /* 36: Last inode change time. */ + [MarshalAs(UnmanagedType.ByValArray, SizeConst = NDADDR)] + public ufs_daddr_t di_db; /* 40: Direct disk blocks. */ + [MarshalAs(UnmanagedType.ByValArray, SizeConst = NIADDR)] + public ufs_daddr_t di_ib; /* 88: Indirect disk blocks. */ + public uint di_flags; /* 100: Status flags (chflags). */ + public uint di_blocks; /* 104: Blocks actually held. */ + public int di_gen; /* 108: Generation number. */ + public uint di_uid; /* 112: File owner. */ + public uint di_gid; /* 116: File group. */ + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] + public int di_spare; /* 120: Reserved; currently unused */ + } + + + public dump() + { + Name = "dump(8) Plugin"; + PluginUUID = new Guid("E53B4D28-C858-4800-B092-DDAE80D361B9"); + CurrentEncoding = Encoding.GetEncoding("iso-8859-15"); + } + + public dump(ImagePlugins.ImagePlugin imagePlugin, Partition partition, Encoding encoding) + { + Name = "dump(8) Plugin"; + PluginUUID = new Guid("E53B4D28-C858-4800-B092-DDAE80D361B9"); + if(encoding == null) + CurrentEncoding = Encoding.GetEncoding("iso-8859-15"); + else + CurrentEncoding = encoding; + } + + public override bool Identify(ImagePlugins.ImagePlugin imagePlugin, Partition partition) + { + if(imagePlugin.GetSectorSize() < 512) + return false; + + // It should be start of a tape or floppy or file + if(partition.Start != 0) + return false; + + spcl16 oldHdr = new spcl16(); + spcl_aix aixHdr = new spcl_aix(); + s_spcl newHdr = new s_spcl(); + + uint sbSize = (uint)((Marshal.SizeOf(newHdr)) / imagePlugin.GetSectorSize()); + if((Marshal.SizeOf(newHdr)) % imagePlugin.GetSectorSize() != 0) + sbSize++; + + byte[] sector = imagePlugin.ReadSectors(partition.Start, sbSize); + if(sector.Length < Marshal.SizeOf(newHdr)) + return false; + + IntPtr oldPtr = Marshal.AllocHGlobal(Marshal.SizeOf(oldHdr)); + Marshal.Copy(sector, 0, oldPtr, Marshal.SizeOf(oldHdr)); + oldHdr = (spcl16)Marshal.PtrToStructure(oldPtr, typeof(spcl16)); + Marshal.FreeHGlobal(oldPtr); + + IntPtr aixPtr = Marshal.AllocHGlobal(Marshal.SizeOf(aixHdr)); + Marshal.Copy(sector, 0, aixPtr, Marshal.SizeOf(aixHdr)); + aixHdr = (spcl_aix)Marshal.PtrToStructure(aixPtr, typeof(spcl_aix)); + Marshal.FreeHGlobal(aixPtr); + + IntPtr newPtr = Marshal.AllocHGlobal(Marshal.SizeOf(newHdr)); + Marshal.Copy(sector, 0, newPtr, Marshal.SizeOf(newHdr)); + newHdr = (s_spcl)Marshal.PtrToStructure(newPtr, typeof(s_spcl)); + Marshal.FreeHGlobal(newPtr); + + DicConsole.DebugWriteLine("dump(8) plugin", "old magic = 0x{0:X8}", oldHdr.c_magic); + DicConsole.DebugWriteLine("dump(8) plugin", "aix magic = 0x{0:X8}", aixHdr.c_magic); + DicConsole.DebugWriteLine("dump(8) plugin", "new magic = 0x{0:X8}", newHdr.c_magic); + + return oldHdr.c_magic == OFS_MAGIC || aixHdr.c_magic == XIX_MAGIC || aixHdr.c_magic == XIX_CIGAM || + newHdr.c_magic == OFS_MAGIC || newHdr.c_magic == NFS_MAGIC || + newHdr.c_magic == OFS_CIGAM || newHdr.c_magic == NFS_CIGAM || + newHdr.c_magic == UFS2_MAGIC || newHdr.c_magic == UFS2_CIGAM; + } + + public override void GetInformation(ImagePlugins.ImagePlugin imagePlugin, Partition partition, out string information) + { + information = ""; + if(imagePlugin.GetSectorSize() < 512) + return; + + if(partition.Start != 0) + return; + + spcl16 oldHdr = new spcl16(); + spcl_aix aixHdr = new spcl_aix(); + s_spcl newHdr = new s_spcl(); + + uint sbSize = (uint)((Marshal.SizeOf(newHdr)) / imagePlugin.GetSectorSize()); + if((Marshal.SizeOf(newHdr)) % imagePlugin.GetSectorSize() != 0) + sbSize++; + + byte[] sector = imagePlugin.ReadSectors(partition.Start, sbSize); + if(sector.Length < Marshal.SizeOf(newHdr)) + return; + + IntPtr oldPtr = Marshal.AllocHGlobal(Marshal.SizeOf(oldHdr)); + Marshal.Copy(sector, 0, oldPtr, Marshal.SizeOf(oldHdr)); + oldHdr = (spcl16)Marshal.PtrToStructure(oldPtr, typeof(spcl16)); + Marshal.FreeHGlobal(oldPtr); + + IntPtr aixPtr = Marshal.AllocHGlobal(Marshal.SizeOf(aixHdr)); + Marshal.Copy(sector, 0, aixPtr, Marshal.SizeOf(aixHdr)); + aixHdr = (spcl_aix)Marshal.PtrToStructure(aixPtr, typeof(spcl_aix)); + Marshal.FreeHGlobal(aixPtr); + + IntPtr newPtr = Marshal.AllocHGlobal(Marshal.SizeOf(newHdr)); + Marshal.Copy(sector, 0, newPtr, Marshal.SizeOf(newHdr)); + newHdr = (s_spcl)Marshal.PtrToStructure(newPtr, typeof(s_spcl)); + Marshal.FreeHGlobal(newPtr); + + bool useOld = false; + bool useAix = false; + bool useNew = false; + + if(newHdr.c_magic == OFS_MAGIC || newHdr.c_magic == NFS_MAGIC || + newHdr.c_magic == OFS_CIGAM || newHdr.c_magic == NFS_CIGAM || + newHdr.c_magic == UFS2_MAGIC || newHdr.c_magic == UFS2_CIGAM) + { + useNew = true; + + if(newHdr.c_magic == OFS_CIGAM || newHdr.c_magic == NFS_CIGAM || + newHdr.c_magic == UFS2_CIGAM) + newHdr = BigEndianMarshal.ByteArrayToStructureBigEndian(sector); + } + else if(aixHdr.c_magic == XIX_MAGIC || aixHdr.c_magic == XIX_CIGAM) + { + useAix = true; + + if(aixHdr.c_magic == XIX_CIGAM) + aixHdr = BigEndianMarshal.ByteArrayToStructureBigEndian(sector); + } + else if(oldHdr.c_magic == OFS_MAGIC) + { + useOld = true; + + // Swap PDP-11 endian + oldHdr.c_date = (int)Swapping.PDPFromLittleEndian((uint)oldHdr.c_date); + oldHdr.c_ddate = (int)Swapping.PDPFromLittleEndian((uint)oldHdr.c_ddate); + } + else + { + information = "Could not read dump(8) header block"; + return; + } + + StringBuilder sb = new StringBuilder(); + + xmlFSType = new Schemas.FileSystemType + { + ClusterSize = 1024, + Clusters = (long)(partition.Size / 1024) + }; + + if(useOld) + { + xmlFSType.Type = "Old 16-bit dump(8)"; + sb.AppendLine(xmlFSType.Type); + if(oldHdr.c_date > 0) + { + xmlFSType.CreationDate = DateHandlers.UNIXToDateTime(oldHdr.c_date); + xmlFSType.CreationDateSpecified = true; + sb.AppendFormat("Dump created on {0}", xmlFSType.CreationDate).AppendLine(); + } + if(oldHdr.c_ddate > 0) + { + xmlFSType.BackupDate = DateHandlers.UNIXToDateTime(oldHdr.c_ddate); + xmlFSType.BackupDateSpecified = true; + sb.AppendFormat("Previous dump created on {0}", xmlFSType.BackupDate).AppendLine(); + } + sb.AppendFormat("Dump volume number: {0}", oldHdr.c_volume).AppendLine(); + } + else if(useAix) + { + xmlFSType.Type = "AIX dump(8)"; + sb.AppendLine(xmlFSType.Type); + if(aixHdr.c_date > 0) + { + xmlFSType.CreationDate = DateHandlers.UNIXToDateTime(aixHdr.c_date); + xmlFSType.CreationDateSpecified = true; + sb.AppendFormat("Dump created on {0}", xmlFSType.CreationDate).AppendLine(); + } + if(aixHdr.c_ddate > 0) + { + xmlFSType.BackupDate = DateHandlers.UNIXToDateTime(aixHdr.c_ddate); + xmlFSType.BackupDateSpecified = true; + sb.AppendFormat("Previous dump created on {0}", xmlFSType.BackupDate).AppendLine(); + } + sb.AppendFormat("Dump volume number: {0}", aixHdr.c_volume).AppendLine(); + } + else if(useNew) + { + xmlFSType.Type = "dump(8)"; + sb.AppendLine(xmlFSType.Type); + if(newHdr.c_ndate > 0) + { + xmlFSType.CreationDate = DateHandlers.UNIXToDateTime(newHdr.c_ndate); + xmlFSType.CreationDateSpecified = true; + sb.AppendFormat("Dump created on {0}", xmlFSType.CreationDate).AppendLine(); + } + else if(newHdr.c_date > 0) + { + xmlFSType.CreationDate = DateHandlers.UNIXToDateTime(newHdr.c_date); + xmlFSType.CreationDateSpecified = true; + sb.AppendFormat("Dump created on {0}", xmlFSType.CreationDate).AppendLine(); + } + if(newHdr.c_nddate > 0) + { + xmlFSType.BackupDate = DateHandlers.UNIXToDateTime(newHdr.c_nddate); + xmlFSType.BackupDateSpecified = true; + sb.AppendFormat("Previous dump created on {0}", xmlFSType.BackupDate).AppendLine(); + } + else if(newHdr.c_ddate > 0) + { + xmlFSType.BackupDate = DateHandlers.UNIXToDateTime(newHdr.c_ddate); + xmlFSType.BackupDateSpecified = true; + sb.AppendFormat("Previous dump created on {0}", xmlFSType.BackupDate).AppendLine(); + } + sb.AppendFormat("Dump volume number: {0}", newHdr.c_volume).AppendLine(); + sb.AppendFormat("Dump level: {0}", newHdr.c_level).AppendLine(); + string dumpname = StringHandlers.CToString(newHdr.c_label); + if(!string.IsNullOrEmpty(dumpname)) + { + XmlFSType.VolumeName = dumpname; + sb.AppendFormat("Dump label: {0}", dumpname).AppendLine(); + } + + string str = StringHandlers.CToString(newHdr.c_filesys); + if(!string.IsNullOrEmpty(str)) + sb.AppendFormat("Dumped filesystem name: {0}", str).AppendLine(); + str = StringHandlers.CToString(newHdr.c_dev); + if(!string.IsNullOrEmpty(str)) + sb.AppendFormat("Dumped device: {0}", str).AppendLine(); + str = StringHandlers.CToString(newHdr.c_host); + if(!string.IsNullOrEmpty(str)) + sb.AppendFormat("Dump hostname: {0}", str).AppendLine(); + } + + 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; + } + } +} \ No newline at end of file diff --git a/README.md b/README.md index 85c04b267..bfa3ac789 100644 --- a/README.md +++ b/README.md @@ -131,6 +131,7 @@ Supported file systems for identification and information only * Cram file system * DEC RT-11 file system * DEC Files-11 (only checked with On Disk Structure 2, ODS-2) +* dump(8) (Old historic BSD, AIX, UFS and UFS2 types) * ECMA-67: 130mm Flexible Disk Cartridge Labelling and File Structure for Information Interchange * Flash-Friendly File System (F2FS) * Fossil file system (from Plan9) diff --git a/TODO b/TODO index 34a9ef4ae..9f16a0d96 100644 --- a/TODO +++ b/TODO @@ -2,7 +2,6 @@ --- Add support for DiscFerret images --- Add support for Kryoflux images --- Add support for XPACK images ---- Add support for dump(8) images Filesystem plugins: --- Add support for NwFS