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