From 1781df14515cd9c9c6fba9bf83be3e8eb292aceb Mon Sep 17 00:00:00 2001 From: Natalia Portillo Date: Tue, 7 Aug 2012 06:20:13 +0000 Subject: [PATCH] * SysV.cs: * FileSystemIDandChk.csproj: Added support for System V filesystem * MBR.cs: UnixWare is not the only one using the UNIX disklabel Corrected UNIX disklabel sector. Enhanced UNIX disklabel with structs and consts. Added support for old UNIX disklabels. * Swapping.cs: Added code for middle-endian (PDP-endian) git-svn-id: svn://claunia.com/FileSystemIDandChk@22 17725271-3d32-4980-a8cb-9ff532f270ba --- FileSystemIDandChk/ChangeLog | 15 + FileSystemIDandChk/FileSystemIDandChk.csproj | 1 + FileSystemIDandChk/PartPlugins/MBR.cs | 309 +++++++-- FileSystemIDandChk/Plugins/SysV.cs | 624 +++++++++++++++++++ FileSystemIDandChk/Swapping.cs | 10 + 5 files changed, 918 insertions(+), 41 deletions(-) create mode 100644 FileSystemIDandChk/Plugins/SysV.cs diff --git a/FileSystemIDandChk/ChangeLog b/FileSystemIDandChk/ChangeLog index 6f814a1d..147f5125 100644 --- a/FileSystemIDandChk/ChangeLog +++ b/FileSystemIDandChk/ChangeLog @@ -1,3 +1,18 @@ +2012-08-07 Natalia Portillo + + * Plugins/SysV.cs: + * FileSystemIDandChk.csproj: + Added support for System V filesystem + + * PartPlugins/MBR.cs: + UnixWare is not the only one using the UNIX disklabel + Corrected UNIX disklabel sector. + Enhanced UNIX disklabel with structs and consts. + Added support for old UNIX disklabels. + + * Swapping.cs: + Added code for middle-endian (PDP-endian) + 2012-08-06 Natalia Portillo * Plugins/UNIXBFS.cs: diff --git a/FileSystemIDandChk/FileSystemIDandChk.csproj b/FileSystemIDandChk/FileSystemIDandChk.csproj index 78dac517..eacdb32f 100644 --- a/FileSystemIDandChk/FileSystemIDandChk.csproj +++ b/FileSystemIDandChk/FileSystemIDandChk.csproj @@ -66,6 +66,7 @@ + diff --git a/FileSystemIDandChk/PartPlugins/MBR.cs b/FileSystemIDandChk/PartPlugins/MBR.cs index 337e5018..066530b0 100644 --- a/FileSystemIDandChk/PartPlugins/MBR.cs +++ b/FileSystemIDandChk/PartPlugins/MBR.cs @@ -135,33 +135,83 @@ namespace FileSystemIDandChk.PartPlugins valid=true; break; } - case 0x63: // UnixWare disklabel + case 0x63: // UNIX disklabel { UInt32 magic; - br.BaseStream.Seek(4, SeekOrigin.Current); + br.BaseStream.Seek(29*0x200 + 4, SeekOrigin.Current); // Starts on sector 29 of partition magic = br.ReadUInt32(); - if(magic == 0xCA5E600D) + if(magic == UNIXDiskLabel_MAGIC) { - br.BaseStream.Seek(164, SeekOrigin.Current); - Int16 no_parts = br.ReadInt16(); - br.BaseStream.Seek(42, SeekOrigin.Current); - - for(int j = 0; j < no_parts; j++) + UNIXDiskLabel dl = new UNIXDiskLabel(); + bool isNewDL = false; + + dl.version = br.ReadUInt32(); + dl.serial = StringHandlers.CToString(br.ReadBytes(12)); + dl.cyls = br.ReadUInt32(); + dl.trks = br.ReadUInt32(); + dl.secs = br.ReadUInt32(); + dl.bps = br.ReadUInt32(); + dl.start = br.ReadUInt32(); + dl.unknown1 = br.ReadBytes(48); + dl.alt_tbl = br.ReadUInt32(); + dl.alt_len = br.ReadUInt32(); + dl.phys_cyl = br.ReadUInt32(); + + if(dl.phys_cyl != UNIXVTOC_MAGIC) // Old version VTOC starts here { - Int16 unx_type = br.ReadInt16(); - br.BaseStream.Seek(2, SeekOrigin.Current); - Partition part = new Partition(); - part.PartitionStart = br.ReadInt32() * 512; - part.PartitionLength = br.ReadInt32() * 512; - part.PartitionSequence = counter; - part.PartitionType = String.Format("Unixware: {0}", unx_type); - part.PartitionDescription = "UnixWare slice."; + isNewDL = true; + dl.phys_trk = br.ReadUInt32(); + dl.phys_sec = br.ReadUInt32(); + dl.phys_bytes = br.ReadUInt32(); + dl.unknown2 = br.ReadUInt32(); + dl.unknown3 = br.ReadUInt32(); + dl.pad = br.ReadBytes(48); + } + else + br.BaseStream.Seek(-4, SeekOrigin.Current); // Return to old VTOC magic + + UNIXVTOC vtoc = new UNIXVTOC(); + + vtoc.magic = br.ReadUInt32(); - if(unx_type!=0) + if(vtoc.magic == UNIXVTOC_MAGIC) + { + vtoc.version = br.ReadUInt32(); + vtoc.name = StringHandlers.CToString(br.ReadBytes(8)); + vtoc.slices = br.ReadUInt16(); + vtoc.unknown = br.ReadUInt16(); + vtoc.reserved = br.ReadBytes(40); + + for(int j = 0; j < vtoc.slices; j++) { - partitions.Add(part); - counter++; + UNIXVTOCEntry vtoc_ent = new UNIXVTOCEntry(); + + vtoc_ent.tag = br.ReadUInt16(); + vtoc_ent.flags = br.ReadUInt16(); + vtoc_ent.start = br.ReadUInt32(); + vtoc_ent.length = br.ReadUInt32(); + + if((vtoc_ent.flags & 0x200) == 0x200 && vtoc_ent.tag != UNIX_TAG_EMPTY && vtoc_ent.tag != UNIX_TAG_WHOLE) + { + Partition part = new Partition(); + part.PartitionStart = vtoc_ent.start * dl.bps; + part.PartitionLength = vtoc_ent.length * dl.bps; + part.PartitionSequence = counter; + part.PartitionType = String.Format("UNIX: {0}", decodeUNIXTAG(vtoc_ent.tag, isNewDL)); + + string info = ""; + + if((vtoc_ent.flags & 0x01) == 0x01) + info += " (do not mount)"; + if((vtoc_ent.flags & 0x10) == 0x10) + info += " (do not mount)"; + + part.PartitionDescription = "UNIX slice" + info + "."; + + partitions.Add(part); + counter++; + } } } } @@ -386,38 +436,88 @@ namespace FileSystemIDandChk.PartPlugins ext_valid=true; break; } - case 0x63: // UnixWare disklabel + case 0x63: // UNIX disklabel { UInt32 magic; - br.BaseStream.Seek(4, SeekOrigin.Current); + br.BaseStream.Seek(29*0x200 + 4, SeekOrigin.Current); // Starts on sector 29 of partition magic = br.ReadUInt32(); - - if(magic == 0xCA5E600D) - { - br.BaseStream.Seek(164, SeekOrigin.Current); - Int16 no_parts = br.ReadInt16(); - br.BaseStream.Seek(42, SeekOrigin.Current); - for(int j = 0; j < no_parts; j++) - { - Int16 unx_type = br.ReadInt16(); - br.BaseStream.Seek(2, SeekOrigin.Current); - Partition part = new Partition(); - part.PartitionStart = br.ReadInt32() * 512; - part.PartitionLength = br.ReadInt32() * 512; - part.PartitionSequence = counter; - part.PartitionType = String.Format("Unixware: {0}", unx_type); - part.PartitionDescription = "UnixWare slice."; + if(magic == UNIXDiskLabel_MAGIC) + { + UNIXDiskLabel dl = new UNIXDiskLabel(); + bool isNewDL = false; - if(unx_type!=0) + dl.version = br.ReadUInt32(); + dl.serial = StringHandlers.CToString(br.ReadBytes(12)); + dl.cyls = br.ReadUInt32(); + dl.trks = br.ReadUInt32(); + dl.secs = br.ReadUInt32(); + dl.bps = br.ReadUInt32(); + dl.start = br.ReadUInt32(); + dl.unknown1 = br.ReadBytes(48); + dl.alt_tbl = br.ReadUInt32(); + dl.alt_len = br.ReadUInt32(); + dl.phys_cyl = br.ReadUInt32(); + + if(dl.phys_cyl != UNIXVTOC_MAGIC) // Old version VTOC starts here + { + isNewDL = true; + dl.phys_trk = br.ReadUInt32(); + dl.phys_sec = br.ReadUInt32(); + dl.phys_bytes = br.ReadUInt32(); + dl.unknown2 = br.ReadUInt32(); + dl.unknown3 = br.ReadUInt32(); + dl.pad = br.ReadBytes(48); + } + else + br.BaseStream.Seek(-4, SeekOrigin.Current); // Return to old VTOC magic + + UNIXVTOC vtoc = new UNIXVTOC(); + + vtoc.magic = br.ReadUInt32(); + + if(vtoc.magic == UNIXVTOC_MAGIC) + { + vtoc.version = br.ReadUInt32(); + vtoc.name = StringHandlers.CToString(br.ReadBytes(8)); + vtoc.slices = br.ReadUInt16(); + vtoc.unknown = br.ReadUInt16(); + vtoc.reserved = br.ReadBytes(40); + + for(int j = 0; j < vtoc.slices; j++) { - partitions.Add(part); - counter++; + UNIXVTOCEntry vtoc_ent = new UNIXVTOCEntry(); + + vtoc_ent.tag = br.ReadUInt16(); + vtoc_ent.flags = br.ReadUInt16(); + vtoc_ent.start = br.ReadUInt32(); + vtoc_ent.length = br.ReadUInt32(); + + if((vtoc_ent.flags & 0x200) == 0x200 && vtoc_ent.tag != UNIX_TAG_EMPTY && vtoc_ent.tag != UNIX_TAG_WHOLE) + { + Partition part = new Partition(); + part.PartitionStart = vtoc_ent.start * dl.bps; + part.PartitionLength = vtoc_ent.length * dl.bps; + part.PartitionSequence = counter; + part.PartitionType = String.Format("UNIX: {0}", decodeUNIXTAG(vtoc_ent.tag, isNewDL)); + + string info = ""; + + if((vtoc_ent.flags & 0x01) == 0x01) + info += " (do not mount)"; + if((vtoc_ent.flags & 0x10) == 0x10) + info += " (do not mount)"; + + part.PartitionDescription = "UNIX slice" + info + "."; + + partitions.Add(part); + counter++; + } } } } else - valid = true; + ext_valid = true; break; } case 0x82: @@ -912,5 +1012,132 @@ namespace FileSystemIDandChk.PartPlugins public UInt32 lba_start; // Starting absolute sector public UInt32 lba_sectors; // Total sectors } + + private const UInt32 UNIXDiskLabel_MAGIC = 0xCA5E600D; + private const UInt32 UNIXVTOC_MAGIC = 0x600DDEEE; // Same as Solaris VTOC + + private struct UNIXDiskLabel + { + public UInt32 type; // Drive type, seems always 0 + public UInt32 magic; // UNIXDiskLabel_MAGIC + public UInt32 version; // Only seen 1 + public string serial; // 12 bytes, serial number of the device + public UInt32 cyls; // data cylinders per device + public UInt32 trks; // data tracks per cylinder + public UInt32 secs; // data sectors per track + public UInt32 bps; // data bytes per sector + public UInt32 start; // first sector of this partition + public byte[] unknown1; // 48 bytes + public UInt32 alt_tbl; // byte offset of alternate table + public UInt32 alt_len; // byte length of alternate table + // From onward here, is not on old version + public UInt32 phys_cyl; // physical cylinders per device + public UInt32 phys_trk; // physical tracks per cylinder + public UInt32 phys_sec; // physical sectors per track + public UInt32 phys_bytes; // physical bytes per sector + public UInt32 unknown2; // + public UInt32 unknown3; // + public byte[] pad; // 32bytes + } + + private struct UNIXVTOC + { + public UInt32 magic; // UNIXVTOC_MAGIC + public UInt32 version; // 1 + public string name; // 8 bytes + public UInt32 slices; // # of slices + public UInt32 unknown; // + public byte[] reserved; // 40 bytes + } + + private struct UNIXVTOCEntry + { + public UInt16 tag; // TAG + public UInt16 flags; // Flags (see below) + public UInt32 start; // Start sector + public UInt32 length; // Length of slice in sectors + } + + private const UInt16 UNIX_TAG_EMPTY = 0x0000; // empty + private const UInt16 UNIX_TAG_BOOT = 0x0001; // boot + private const UInt16 UNIX_TAG_ROOT = 0x0002; // root + private const UInt16 UNIX_TAG_SWAP = 0x0003; // swap + private const UInt16 UNIX_TAG_USER = 0x0004; // /usr + private const UInt16 UNIX_TAG_WHOLE = 0x0005; // whole disk + private const UInt16 UNIX_TAG_STAND = 0x0006; // stand partition ?? + private const UInt16 UNIX_TAG_ALT_S = 0x0006; // alternate sector space + private const UInt16 UNIX_TAG_VAR = 0x0007; // /var + private const UInt16 UNIX_TAG_OTHER = 0x0007; // non UNIX + private const UInt16 UNIX_TAG_HOME = 0x0008; // /home + private const UInt16 UNIX_TAG_ALT_T = 0x0008; // alternate track space + private const UInt16 UNIX_TAG_ALT_ST = 0x0009; // alternate sector track + private const UInt16 UNIX_TAG_NEW_STAND = 0x0009; // stand partition ?? + private const UInt16 UNIX_TAG_CACHE = 0x000A; // cache + private const UInt16 UNIX_TAG_NEW_VAR = 0x000A; // /var + private const UInt16 UNIX_TAG_RESERVED = 0x000B; // reserved + private const UInt16 UNIX_TAG_NEW_HOME = 0x000B; // /home + private const UInt16 UNIX_TAG_DUMP = 0x000C; // dump partition + private const UInt16 UNIX_TAG_NEW_ALT_ST = 0x000D; // alternate sector track + private const UInt16 UNIX_TAG_VM_PUBLIC = 0x000E; // volume mgt public partition + private const UInt16 UNIX_TAG_VM_PRIVATE = 0x000F; // volume mgt private partition + + private string decodeUNIXTAG(UInt16 type, bool isNew) + { + switch(type) + { + case UNIX_TAG_EMPTY: + return "Unused"; + case UNIX_TAG_BOOT: + return "Boot"; + case UNIX_TAG_ROOT: + return "/"; + case UNIX_TAG_SWAP: + return "Swap"; + case UNIX_TAG_USER: + return "/usr"; + case UNIX_TAG_WHOLE: + return "Whole disk"; + case UNIX_TAG_STAND: + if(isNew) + return "Stand"; + else + return "Alternate sector space"; + case UNIX_TAG_VAR: + if(isNew) + return "/var"; + else + return "non UNIX"; + case UNIX_TAG_HOME: + if(isNew) + return "/home"; + else + return "Alternate track space"; + case UNIX_TAG_ALT_ST: + if(isNew) + return "Alternate sector track"; + else + return "Stand"; + case UNIX_TAG_CACHE: + if(isNew) + return "Cache"; + else + return "/var"; + case UNIX_TAG_RESERVED: + if(isNew) + return "Reserved"; + else + return "/home"; + case UNIX_TAG_DUMP: + return "dump"; + case UNIX_TAG_NEW_ALT_ST: + return "Alternate sector track"; + case UNIX_TAG_VM_PUBLIC: + return "volume mgt public partition"; + case UNIX_TAG_VM_PRIVATE: + return "volume mgt private partition"; + default: + return String.Format ("Unknown TAG: 0x{0:X4}", type); + } + } } } \ No newline at end of file diff --git a/FileSystemIDandChk/Plugins/SysV.cs b/FileSystemIDandChk/Plugins/SysV.cs new file mode 100644 index 00000000..c5afea3a --- /dev/null +++ b/FileSystemIDandChk/Plugins/SysV.cs @@ -0,0 +1,624 @@ +using System; +using System.IO; +using System.Text; +using FileSystemIDandChk; + +namespace FileSystemIDandChk.Plugins +{ + class SysVfs : Plugin + { + private const UInt32 XENIX_MAGIC = 0x002B5544; + private const UInt32 XENIX_CIGAM = 0x44552B00; + private const UInt32 SYSV_MAGIC = 0xFD187E20; + private const UInt32 SYSV_CIGAM = 0xFD187E20; + // Rest have no magic. + // Per a Linux kernel, Coherent fs has following: + private const string COH_FNAME = "nonamexxxxx "; + private const string COH_FPACK = "nopackxxxxx\n"; + // SCO AFS + private const UInt16 SCO_NFREE = 0xFFFF; + // UNIX 7th Edition has nothing to detect it, so check for a valid filesystem is a must :( + private const UInt16 V7_NICINOD = 100; + private const UInt16 V7_NICFREE = 50; + private const UInt32 V7_MAXSIZE = 0x00FFFFFF; + + public SysVfs(PluginBase Core) + { + base.Name = "UNIX System V filesystem"; + base.PluginUUID = new Guid("9B8D016A-8561-400E-A12A-A198283C211D"); + } + + public override bool Identify(FileStream stream, long offset) + { + UInt32 magic; + string s_fname, s_fpack; + UInt16 s_nfree, s_ninode; + UInt32 s_fsize; + + BinaryReader br = new BinaryReader(stream); + + /*for(int j = 0; j<=(br.BaseStream.Length/0x200); j++) + { + br.BaseStream.Seek(offset + j*0x200 + 0x1F8, SeekOrigin.Begin); // System V magic location + magic = br.ReadUInt32(); + + if(magic == SYSV_MAGIC || magic == SYSV_CIGAM) + Console.WriteLine("0x{0:X8}: 0x{1:X8} FOUND", br.BaseStream.Position-4, magic); + else + Console.WriteLine("0x{0:X8}: 0x{1:X8}", br.BaseStream.Position-4, magic); + }*/ + + /*UInt32 number; + br.BaseStream.Seek(offset+0x3A00, SeekOrigin.Begin); + while((br.BaseStream.Position) <= (offset+0x3C00)) + { + number = br.ReadUInt32(); + + Console.WriteLine("@{0:X8}: 0x{1:X8} ({1})", br.BaseStream.Position-offset-4, number); + }*/ + + for(int i = 0; i<=4; i++) // Check on 0x0000, 0x0200, 0x0600, 0x0800 + offset + { + if((ulong)br.BaseStream.Length <= (ulong)(offset + i*0x200 + 0x400)) // Stream must be bigger than SB location + SB size + offset + return false; + + br.BaseStream.Seek(offset + i*0x200 + 0x3F8, SeekOrigin.Begin); // XENIX magic location + magic = br.ReadUInt32(); + + if(magic == XENIX_MAGIC || magic == XENIX_CIGAM) + return true; + + br.BaseStream.Seek(offset + i*0x200 + 0x1F8, SeekOrigin.Begin); // System V magic location + magic = br.ReadUInt32(); + + if(magic == SYSV_MAGIC || magic == SYSV_CIGAM) + return true; + + br.BaseStream.Seek(offset + i*0x200 + 0x1E8, SeekOrigin.Begin); // Coherent UNIX s_fname location + s_fname = StringHandlers.CToString(br.ReadBytes(6)); + s_fpack = StringHandlers.CToString(br.ReadBytes(6)); + + if(s_fname == COH_FNAME || s_fpack == COH_FPACK) + return true; + + // Now try to identify 7th edition + br.BaseStream.Seek(offset + i*0x200 + 0x002, SeekOrigin.Begin); + s_fsize = br.ReadUInt32(); + br.BaseStream.Seek(offset + i*0x200 + 0x006, SeekOrigin.Begin); + s_nfree = br.ReadUInt16(); + br.BaseStream.Seek(offset + i*0x200 + 0x0D0, SeekOrigin.Begin); + s_ninode = br.ReadUInt16(); + + if(s_fsize > 0 && s_fsize < 0xFFFFFFFF && s_nfree > 0 && s_nfree < 0xFFFF && s_ninode > 0 && s_ninode < 0xFFFF) + { + 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 = (UInt16)(s_nfree>>8); + s_ninode = (UInt16)(s_ninode>>8); + } + + if((s_fsize & 0xFF000000) == 0x00 && (s_nfree & 0xFF00) == 0x00 && (s_ninode & 0xFF00) == 0x00) + { + if(s_fsize < V7_MAXSIZE && s_nfree < V7_NICFREE && s_ninode < V7_NICINOD) + { + if((s_fsize * 1024) <= (br.BaseStream.Length-offset) || (s_fsize * 512) <= (br.BaseStream.Length-offset)) + return true; + } + } + } + } + + return false; + } + + public override void GetInformation (FileStream stream, long offset, out string information) + { + information = ""; + + StringBuilder sb = new StringBuilder(); + bool littleendian = true; + EndianAwareBinaryReader eabr = new EndianAwareBinaryReader(stream, littleendian); // Start in little endian until we know what are we handling here + int start; + UInt32 magic; + string s_fname, s_fpack; + UInt16 s_nfree, s_ninode; + UInt32 s_fsize; + bool xenix = false; + bool sysv = false; + bool sysvr2 = false; + bool sysvr4 = false; + bool sys7th = false; + bool coherent = false; + + for(start = 0; start<=4; start++) // Check on 0x0000, 0x0200, 0x0600, 0x0800 + offset + { + eabr.BaseStream.Seek(offset + start*0x200 + 0x3F8, SeekOrigin.Begin); // XENIX magic location + magic = eabr.ReadUInt32(); + + if(magic == XENIX_MAGIC) + { + littleendian = true; + xenix = true; + break; + } + else if(magic == XENIX_CIGAM) + { + littleendian = false; + xenix = true; + break; + } + + eabr.BaseStream.Seek(offset + start*0x200 + 0x1F8, SeekOrigin.Begin); // System V magic location + magic = eabr.ReadUInt32(); + + if(magic == SYSV_MAGIC) + { + littleendian = true; + sysv = true; + break; + } + else if(magic == SYSV_CIGAM) + { + littleendian = false; + sysv = true; + break; + } + + eabr.BaseStream.Seek(offset + start*0x200 + 0x1E8, SeekOrigin.Begin); // Coherent UNIX s_fname location + s_fname = StringHandlers.CToString(eabr.ReadBytes(6)); + s_fpack = StringHandlers.CToString(eabr.ReadBytes(6)); + + if(s_fname == COH_FNAME || s_fpack == COH_FPACK) + { + littleendian = true; // Coherent is in PDP endianness, use helper for that + coherent = true; + break; + } + + // Now try to identify 7th edition + eabr.BaseStream.Seek(offset + start*0x200 + 0x002, SeekOrigin.Begin); + s_fsize = eabr.ReadUInt32(); + eabr.BaseStream.Seek(offset + start*0x200 + 0x006, SeekOrigin.Begin); + s_nfree = eabr.ReadUInt16(); + eabr.BaseStream.Seek(offset + start*0x200 + 0x0D0, SeekOrigin.Begin); + s_ninode = eabr.ReadUInt16(); + + if(s_fsize > 0 && s_fsize < 0xFFFFFFFF && s_nfree > 0 && s_nfree < 0xFFFF && s_ninode > 0 && s_ninode < 0xFFFF) + { + bool byteswapped = false; + 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 = (UInt16)(s_nfree>>8); + s_ninode = (UInt16)(s_ninode>>8); + byteswapped = true; + } + + if((s_fsize & 0xFF000000) == 0x00 && (s_nfree & 0xFF00) == 0x00 && (s_ninode & 0xFF00) == 0x00) + { + if(s_fsize < V7_MAXSIZE && s_nfree < V7_NICFREE && s_ninode < V7_NICINOD) + { + if((s_fsize * 1024) <= (eabr.BaseStream.Length-offset) || (s_fsize * 512) <= (eabr.BaseStream.Length-offset)) + { + sys7th = true; + littleendian = true; + break; + } + } + } + } + } + if(!sys7th && !sysv && !coherent && !xenix) + return; + + if(xenix) + { + eabr = new EndianAwareBinaryReader(stream, littleendian); + XenixSuperBlock xnx_sb = new XenixSuperBlock(); + eabr.BaseStream.Seek(offset + start*0x200, SeekOrigin.Begin); + xnx_sb.s_isize = eabr.ReadUInt16(); + xnx_sb.s_fsize = eabr.ReadUInt32(); + xnx_sb.s_nfree = eabr.ReadUInt16(); + eabr.BaseStream.Seek(400, SeekOrigin.Current); // Skip free block list + xnx_sb.s_ninode = eabr.ReadUInt16(); + eabr.BaseStream.Seek(200, SeekOrigin.Current); // Skip free inode list + xnx_sb.s_flock = eabr.ReadByte(); + xnx_sb.s_ilock = eabr.ReadByte(); + xnx_sb.s_fmod = eabr.ReadByte(); + xnx_sb.s_ronly = eabr.ReadByte(); + xnx_sb.s_time = eabr.ReadUInt32(); + xnx_sb.s_tfree = eabr.ReadUInt32(); + xnx_sb.s_tinode = eabr.ReadUInt16(); + xnx_sb.s_cylblks = eabr.ReadUInt16(); + xnx_sb.s_gapblks = eabr.ReadUInt16(); + xnx_sb.s_dinfo0 = eabr.ReadUInt16(); + xnx_sb.s_dinfo1 = eabr.ReadUInt16(); + xnx_sb.s_fname = StringHandlers.CToString(eabr.ReadBytes(6)); + xnx_sb.s_fpack = StringHandlers.CToString(eabr.ReadBytes(6)); + xnx_sb.s_clean = eabr.ReadByte(); + xnx_sb.s_magic = eabr.ReadUInt32(); + eabr.BaseStream.Seek(371, SeekOrigin.Current); // Skip fill zone + xnx_sb.s_type = eabr.ReadUInt32(); + + UInt32 bs = 512; + sb.AppendLine("XENIX filesystem"); + switch(xnx_sb.s_type) + { + case 1: + sb.AppendLine("512 bytes per block"); + break; + case 2: + sb.AppendLine("1024 bytes per block"); + bs=1024; + break; + case 3: + sb.AppendLine("2048 bytes per block"); + bs=2048; + break; + default: + sb.AppendFormat("Unknown s_type value: 0x{0:X8}", xnx_sb.s_type).AppendLine(); + break; + } + 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.UNIXUnsignedToDateTime(xnx_sb.s_time)).AppendLine(); + sb.AppendFormat("Volume name: {0}", xnx_sb.s_fname).AppendLine(); + 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"); + } + + if(sysv) + { + eabr = new EndianAwareBinaryReader(stream, littleendian); + UInt16 pad0, pad1, pad2, pad3; + + eabr.BaseStream.Seek(offset + start*0x200 + 0x002, SeekOrigin.Begin); // First padding + pad0 = eabr.ReadUInt16(); + eabr.BaseStream.Seek(offset + start*0x200 + 0x00A, SeekOrigin.Begin); // Second padding + pad1 = eabr.ReadUInt16(); + eabr.BaseStream.Seek(offset + start*0x200 + 0x0D6, SeekOrigin.Begin); // Third padding + pad2 = eabr.ReadUInt16(); + eabr.BaseStream.Seek(offset + start*0x200 + 0x1B6, SeekOrigin.Begin); // Fourth padding + pad3 = eabr.ReadUInt16(); + + // This detection is not working as expected + if(pad0 == 0 && pad1 == 0 && pad2 == 0) + sysvr4 = true; + else + sysvr2 = true; + + SystemVRelease4SuperBlock sysv_sb = new SystemVRelease4SuperBlock(); + eabr.BaseStream.Seek(offset + start*0x200, SeekOrigin.Begin); + sysv_sb.s_isize = eabr.ReadUInt16(); + if(sysvr4) + eabr.BaseStream.Seek(2, SeekOrigin.Current); // Skip padding + sysv_sb.s_fsize = eabr.ReadUInt32(); + sysv_sb.s_nfree = eabr.ReadUInt16(); + if(sysvr4) + eabr.BaseStream.Seek(2, SeekOrigin.Current); // Skip padding + eabr.BaseStream.Seek(200, SeekOrigin.Current); // Skip free block list + sysv_sb.s_ninode = eabr.ReadUInt16(); + if(sysvr4) + eabr.BaseStream.Seek(2, SeekOrigin.Current); // Skip padding + eabr.BaseStream.Seek(200, SeekOrigin.Current); // Skip free inode list + sysv_sb.s_flock = eabr.ReadByte(); + sysv_sb.s_ilock = eabr.ReadByte(); + sysv_sb.s_fmod = eabr.ReadByte(); + sysv_sb.s_ronly = eabr.ReadByte(); + sysv_sb.s_time = eabr.ReadUInt32(); + sysv_sb.s_cylblks = eabr.ReadUInt16(); + sysv_sb.s_gapblks = eabr.ReadUInt16(); + sysv_sb.s_dinfo0 = eabr.ReadUInt16(); + sysv_sb.s_dinfo1 = eabr.ReadUInt16(); + sysv_sb.s_tfree = eabr.ReadUInt32(); + sysv_sb.s_tinode = eabr.ReadUInt16(); + if(sysvr4 && pad3 == 0) + eabr.BaseStream.Seek(2, SeekOrigin.Current); // Skip padding + sysv_sb.s_fname = StringHandlers.CToString(eabr.ReadBytes(6)); + sysv_sb.s_fpack = StringHandlers.CToString(eabr.ReadBytes(6)); + if(sysvr4 && pad3 == 0) + eabr.BaseStream.Seek(50, SeekOrigin.Current); // Skip fill zone + else if(sysvr4) + eabr.BaseStream.Seek(50, SeekOrigin.Current); // Skip fill zone + else + eabr.BaseStream.Seek(56, SeekOrigin.Current); // Skip fill zone + sysv_sb.s_state = eabr.ReadUInt32(); + sysv_sb.s_magic = eabr.ReadUInt32(); + sysv_sb.s_type = eabr.ReadUInt32(); + + UInt32 bs = 512; + if(sysvr4) + sb.AppendLine("System V Release 4 filesystem"); + else + sb.AppendLine("System V Release 2 filesystem"); + switch(sysv_sb.s_type) + { + case 1: + sb.AppendLine("512 bytes per block"); + break; + case 2: + sb.AppendLine("1024 bytes per block"); + bs = 1024; + break; + case 3: + sb.AppendLine("2048 bytes per block"); + bs = 2048; + break; + default: + sb.AppendFormat("Unknown s_type value: 0x{0:X8}", sysv_sb.s_type).AppendLine(); + break; + } + 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(); + sb.AppendFormat("Volume name: {0}", sysv_sb.s_fname).AppendLine(); + 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"); + } + + if(coherent) + { + eabr = new EndianAwareBinaryReader(stream, true); + CoherentSuperBlock coh_sb = new CoherentSuperBlock(); + eabr.BaseStream.Seek(offset + start*0x200, SeekOrigin.Begin); + coh_sb.s_isize = eabr.ReadUInt16(); + coh_sb.s_fsize = Swapping.PDPFromLittleEndian(eabr.ReadUInt32()); + coh_sb.s_nfree = eabr.ReadUInt16(); + eabr.BaseStream.Seek(256, SeekOrigin.Current); // Skip free block list + coh_sb.s_ninode = eabr.ReadUInt16(); + eabr.BaseStream.Seek(200, SeekOrigin.Current); // Skip free inode list + coh_sb.s_flock = eabr.ReadByte(); + coh_sb.s_ilock = eabr.ReadByte(); + coh_sb.s_fmod = eabr.ReadByte(); + coh_sb.s_ronly = eabr.ReadByte(); + coh_sb.s_time = Swapping.PDPFromLittleEndian(eabr.ReadUInt32()); + coh_sb.s_tfree = Swapping.PDPFromLittleEndian(eabr.ReadUInt32()); + coh_sb.s_tinode = eabr.ReadUInt16(); + coh_sb.s_int_m = eabr.ReadUInt16(); + coh_sb.s_int_n = eabr.ReadUInt16(); + coh_sb.s_fname = StringHandlers.CToString(eabr.ReadBytes(6)); + coh_sb.s_fpack = StringHandlers.CToString(eabr.ReadBytes(6)); + + sb.AppendLine("Coherent UNIX filesystem"); + 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(); + sb.AppendFormat("Volume name: {0}", coh_sb.s_fname).AppendLine(); + sb.AppendFormat("Pack name: {0}", coh_sb.s_fpack).AppendLine(); + } + + if(sys7th) + { + eabr = new EndianAwareBinaryReader(stream, littleendian); + UNIX7thEditionSuperBlock v7_sb = new UNIX7thEditionSuperBlock(); + eabr.BaseStream.Seek(offset + start*0x200, SeekOrigin.Begin); + v7_sb.s_isize = eabr.ReadUInt16(); + v7_sb.s_fsize = eabr.ReadUInt32(); + v7_sb.s_nfree = eabr.ReadUInt16(); + eabr.BaseStream.Seek(200, SeekOrigin.Current); // Skip free block list + v7_sb.s_ninode = eabr.ReadUInt16(); + eabr.BaseStream.Seek(200, SeekOrigin.Current); // Skip free inode list + v7_sb.s_flock = eabr.ReadByte(); + v7_sb.s_ilock = eabr.ReadByte(); + v7_sb.s_fmod = eabr.ReadByte(); + v7_sb.s_ronly = eabr.ReadByte(); + v7_sb.s_time = eabr.ReadUInt32(); + v7_sb.s_tfree = eabr.ReadUInt32(); + v7_sb.s_tinode = eabr.ReadUInt16(); + v7_sb.s_int_m = eabr.ReadUInt16(); + v7_sb.s_int_n = eabr.ReadUInt16(); + v7_sb.s_fname = StringHandlers.CToString(eabr.ReadBytes(6)); + v7_sb.s_fpack = StringHandlers.CToString(eabr.ReadBytes(6)); + + sb.AppendLine("UNIX 7th Edition filesystem"); + 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(); + sb.AppendFormat("Volume name: {0}", v7_sb.s_fname).AppendLine(); + sb.AppendFormat("Pack name: {0}", v7_sb.s_fpack).AppendLine(); + } + + information = sb.ToString(); + } + + private struct XenixSuperBlock + { + public UInt16 s_isize; // 0x000, index of first data zone + public UInt32 s_fsize; // 0x002, total number of zones of this volume + // the start of the free block list: + public UInt16 s_nfree; // 0x006, blocks in s_free, <=100 + public UInt32[] s_free; // 0x008, 100 entries, first free block list chunk + // the cache of free inodes: + public UInt16 s_ninode; // 0x198, number of inodes in s_inode, <= 100 + public UInt16[] s_inode; // 0x19A, 100 entries, some free inodes + public byte s_flock; // 0x262, free block list manipulation lock + public byte s_ilock; // 0x263, inode cache manipulation lock + public byte s_fmod; // 0x264, superblock modification flag + public byte s_ronly; // 0x265, read-only mounted flag + public UInt32 s_time; // 0x266, time of last superblock update + public UInt32 s_tfree; // 0x26A, total number of free zones + public UInt16 s_tinode; // 0x26E, total number of free inodes + public UInt16 s_cylblks; // 0x270, blocks per cylinder + public UInt16 s_gapblks; // 0x272, blocks per gap + public UInt16 s_dinfo0; // 0x274, device information ?? + public UInt16 s_dinfo1; // 0x276, device information ?? + public string s_fname; // 0x278, 6 bytes, volume name + public string s_fpack; // 0x27E, 6 bytes, pack name + public byte s_clean; // 0x284, 0x46 if volume is clean + public byte[] s_fill; // 0x285, 371 bytes + public UInt32 s_magic; // 0x3F8, magic + public UInt32 s_type; // 0x3FC, filesystem type (1 = 512 bytes/blk, 2 = 1024 bytes/blk, 3 = 2048 bytes/blk) + } + + private struct SystemVRelease4SuperBlock + { + public UInt16 s_isize; // 0x000, index of first data zone + public UInt16 s_pad0; // 0x002, padding + public UInt32 s_fsize; // 0x004, total number of zones of this volume + // the start of the free block list: + public UInt16 s_nfree; // 0x008, blocks in s_free, <=100 + public UInt16 s_pad1; // 0x00A, padding + public UInt32[] s_free; // 0x00C, 50 entries, first free block list chunk + // the cache of free inodes: + public UInt16 s_ninode; // 0x0D4, number of inodes in s_inode, <= 100 + public UInt16 s_pad2; // 0x0D6, padding + public UInt16[] s_inode; // 0x0D8, 100 entries, some free inodes + public byte s_flock; // 0x1A0, free block list manipulation lock + public byte s_ilock; // 0x1A1, inode cache manipulation lock + public byte s_fmod; // 0x1A2, superblock modification flag + public byte s_ronly; // 0x1A3, read-only mounted flag + public UInt32 s_time; // 0x1A4, time of last superblock update + public UInt16 s_cylblks; // 0x1A8, blocks per cylinder + public UInt16 s_gapblks; // 0x1AA, blocks per gap + public UInt16 s_dinfo0; // 0x1AC, device information ?? + public UInt16 s_dinfo1; // 0x1AE, device information ?? + public UInt32 s_tfree; // 0x1B0, total number of free zones + public UInt16 s_tinode; // 0x1B4, total number of free inodes + public UInt16 s_pad3; // 0x1B6, padding + public string s_fname; // 0x1B8, 6 bytes, volume name + public string s_fpack; // 0x1BE, 6 bytes, pack name + public byte[] s_fill; // 0x1C4, 48 bytes + public UInt32 s_state; // 0x1F4, if s_state == (0x7C269D38 - s_time) then filesystem is clean + public UInt32 s_magic; // 0x1F8, magic + public UInt32 s_type; // 0x1FC, filesystem type (1 = 512 bytes/blk, 2 = 1024 bytes/blk) + } + + private struct SystemVRelease2SuperBlock + { + public UInt16 s_isize; // 0x000, index of first data zone + public UInt32 s_fsize; // 0x002, total number of zones of this volume + // the start of the free block list: + public UInt16 s_nfree; // 0x006, blocks in s_free, <=100 + public UInt32[] s_free; // 0x008, 50 entries, first free block list chunk + // the cache of free inodes: + public UInt16 s_ninode; // 0x0D0, number of inodes in s_inode, <= 100 + public UInt16[] s_inode; // 0x0D2, 100 entries, some free inodes + public byte s_flock; // 0x19A, free block list manipulation lock + public byte s_ilock; // 0x19B, inode cache manipulation lock + public byte s_fmod; // 0x19C, superblock modification flag + public byte s_ronly; // 0x19D, read-only mounted flag + public UInt32 s_time; // 0x19E, time of last superblock update + public UInt16 s_cylblks; // 0x1A2, blocks per cylinder + public UInt16 s_gapblks; // 0x1A4, blocks per gap + public UInt16 s_dinfo0; // 0x1A6, device information ?? + public UInt16 s_dinfo1; // 0x1A8, device information ?? + public UInt32 s_tfree; // 0x1AA, total number of free zones + public UInt16 s_tinode; // 0x1AE, total number of free inodes + public string s_fname; // 0x1B0, 6 bytes, volume name + public string s_fpack; // 0x1B6, 6 bytes, pack name + public byte[] s_fill; // 0x1BC, 56 bytes + public UInt32 s_state; // 0x1F4, if s_state == (0x7C269D38 - s_time) then filesystem is clean + public UInt32 s_magic; // 0x1F8, magic + public UInt32 s_type; // 0x1FC, filesystem type (1 = 512 bytes/blk, 2 = 1024 bytes/blk) + } + + private struct UNIX7thEditionSuperBlock + { + public UInt16 s_isize; // 0x000, index of first data zone + public UInt32 s_fsize; // 0x002, total number of zones of this volume + // the start of the free block list: + public UInt16 s_nfree; // 0x006, blocks in s_free, <=100 + public UInt32[] s_free; // 0x008, 50 entries, first free block list chunk + // the cache of free inodes: + public UInt16 s_ninode; // 0x0D0, number of inodes in s_inode, <= 100 + public UInt16[] s_inode; // 0x0D2, 100 entries, some free inodes + public byte s_flock; // 0x19A, free block list manipulation lock + public byte s_ilock; // 0x19B, inode cache manipulation lock + public byte s_fmod; // 0x19C, superblock modification flag + public byte s_ronly; // 0x19D, read-only mounted flag + public UInt32 s_time; // 0x19E, time of last superblock update + public UInt32 s_tfree; // 0x1A2, total number of free zones + public UInt16 s_tinode; // 0x1A6, total number of free inodes + public UInt16 s_int_m; // 0x1A8, interleave factor + public UInt16 s_int_n; // 0x1AA, interleave factor + public string s_fname; // 0x1AC, 6 bytes, volume name + public string s_fpack; // 0x1B2, 6 bytes, pack name + } + + private struct CoherentSuperBlock + { + public UInt16 s_isize; // 0x000, index of first data zone + public UInt32 s_fsize; // 0x002, total number of zones of this volume + // the start of the free block list: + public UInt16 s_nfree; // 0x006, blocks in s_free, <=100 + public UInt32[] s_free; // 0x008, 64 entries, first free block list chunk + // the cache of free inodes: + public UInt16 s_ninode; // 0x108, number of inodes in s_inode, <= 100 + public UInt16[] s_inode; // 0x10A, 100 entries, some free inodes + public byte s_flock; // 0x1D2, free block list manipulation lock + public byte s_ilock; // 0x1D3, inode cache manipulation lock + public byte s_fmod; // 0x1D4, superblock modification flag + public byte s_ronly; // 0x1D5, read-only mounted flag + public UInt32 s_time; // 0x1D6, time of last superblock update + public UInt32 s_tfree; // 0x1DE, total number of free zones + public UInt16 s_tinode; // 0x1E2, total number of free inodes + public UInt16 s_int_m; // 0x1E4, interleave factor + public UInt16 s_int_n; // 0x1E6, interleave factor + public string s_fname; // 0x1E8, 6 bytes, volume name + public string s_fpack; // 0x1EE, 6 bytes, pack name + public UInt32 s_unique; // 0x1F4, zero-filled + } + } +} + diff --git a/FileSystemIDandChk/Swapping.cs b/FileSystemIDandChk/Swapping.cs index 0e901535..cc392331 100644 --- a/FileSystemIDandChk/Swapping.cs +++ b/FileSystemIDandChk/Swapping.cs @@ -59,5 +59,15 @@ namespace FileSystemIDandChk return destination; } + + public static UInt32 PDPFromLittleEndian(UInt32 x) + { + return ((x & 0xffff) << 16) | ((x & 0xffff0000) >> 16); + } + + public static UInt32 PDPFromBigEndian(UInt32 x) + { + return ((x & 0xff00ff) << 8) | ((x & 0xff00ff00) >> 8); + } } }