// /*************************************************************************** // The Disc Image Chef // ---------------------------------------------------------------------------- // // Filename : FFS.cs // Author(s) : Natalia Portillo // // Component : BSD Fast File System plugin. // // --[ Description ] ---------------------------------------------------------- // // Identifies the BSD Fast File System 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.Diagnostics.CodeAnalysis; using System.Linq; using System.Runtime.InteropServices; using System.Text; using DiscImageChef.CommonTypes; using DiscImageChef.CommonTypes.Interfaces; using DiscImageChef.Console; using Schemas; using time_t = System.Int32; using ufs_daddr_t = System.Int32; namespace DiscImageChef.Filesystems { // Using information from Linux kernel headers [SuppressMessage("ReSharper", "InconsistentNaming")] public class FFSPlugin : IFilesystem { const uint block_size = 8192; // FreeBSD specifies starts at byte offsets 0, 8192, 65536 and 262144, but in other cases it's following sectors // Without bootcode const ulong sb_start_floppy = 0; // With bootcode const ulong sb_start_boot = 1; // Dunno, longer boot code const ulong sb_start_long_boot = 8; // Found on AT&T for MD-2D floppieslzio const ulong sb_start_att_dsdd = 14; // Found on hard disks (Atari UNIX e.g.) const ulong sb_start_piggy = 32; // MAGICs // UFS magic const uint UFS_MAGIC = 0x00011954; // Big-endian UFS magic const uint UFS_CIGAM = 0x54190100; // BorderWare UFS const uint UFS_MAGIC_BW = 0x0F242697; // Big-endian BorderWare UFS const uint UFS_CIGAM_BW = 0x9726240F; // UFS2 magic const uint UFS2_MAGIC = 0x19540119; // Big-endian UFS2 magic const uint UFS2_CIGAM = 0x19015419; // Incomplete newfs const uint UFS_BAD_MAGIC = 0x19960408; // Big-endian incomplete newfs const uint UFS_BAD_CIGAM = 0x08049619; public FileSystemType XmlFsType { get; private set; } public Encoding Encoding { get; private set; } public string Name => "BSD Fast File System (aka UNIX File System, UFS)"; public Guid Id => new Guid("CC90D342-05DB-48A8-988C-C1FE000034A3"); public bool Identify(IMediaImage imagePlugin, Partition partition) { if(2 + partition.Start >= partition.End) return false; uint sbSizeInSectors; if(imagePlugin.Info.SectorSize == 2336 || imagePlugin.Info.SectorSize == 2352 || imagePlugin.Info.SectorSize == 2448) sbSizeInSectors = block_size / 2048; else sbSizeInSectors = block_size / imagePlugin.Info.SectorSize; ulong[] locations = { sb_start_floppy, sb_start_boot, sb_start_long_boot, sb_start_piggy, sb_start_att_dsdd, 8192 / imagePlugin.Info.SectorSize, 65536 / imagePlugin.Info.SectorSize, 262144 / imagePlugin.Info.SectorSize }; return locations.Where(loc => partition.End > partition.Start + loc + sbSizeInSectors) .Select(loc => imagePlugin.ReadSectors(partition.Start + loc, sbSizeInSectors)) .Select(ufsSbSectors => BitConverter.ToUInt32(ufsSbSectors, 0x055C)) .Any(magic => magic == UFS_MAGIC || magic == UFS_CIGAM || magic == UFS_MAGIC_BW || magic == UFS_CIGAM_BW || magic == UFS2_MAGIC || magic == UFS2_CIGAM || magic == UFS_BAD_MAGIC || magic == UFS_BAD_CIGAM); } public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, Encoding encoding) { Encoding = encoding ?? Encoding.GetEncoding("iso-8859-15"); information = ""; StringBuilder sbInformation = new StringBuilder(); uint magic = 0; uint sb_size_in_sectors; byte[] ufs_sb_sectors; ulong sb_offset = partition.Start; bool fs_type_42bsd = false; bool fs_type_43bsd = false; bool fs_type_44bsd = false; bool fs_type_ufs = false; bool fs_type_ufs2 = false; bool fs_type_sun = false; bool fs_type_sun86 = false; if(imagePlugin.Info.SectorSize == 2336 || imagePlugin.Info.SectorSize == 2352 || imagePlugin.Info.SectorSize == 2448) sb_size_in_sectors = block_size / 2048; else sb_size_in_sectors = block_size / imagePlugin.Info.SectorSize; ulong[] locations = { sb_start_floppy, sb_start_boot, sb_start_long_boot, sb_start_piggy, sb_start_att_dsdd, 8192 / imagePlugin.Info.SectorSize, 65536 / imagePlugin.Info.SectorSize, 262144 / imagePlugin.Info.SectorSize }; foreach(ulong loc in locations.Where(loc => partition.End > partition.Start + loc + sb_size_in_sectors)) { ufs_sb_sectors = imagePlugin.ReadSectors(partition.Start + loc, sb_size_in_sectors); magic = BitConverter.ToUInt32(ufs_sb_sectors, 0x055C); if(magic == UFS_MAGIC || magic == UFS_CIGAM || magic == UFS_MAGIC_BW || magic == UFS_CIGAM_BW || magic == UFS2_MAGIC || magic == UFS2_CIGAM || magic == UFS_BAD_MAGIC || magic == UFS_BAD_CIGAM) { sb_offset = partition.Start + loc; break; } magic = 0; } if(magic == 0) { information = "Not a UFS filesystem, I shouldn't have arrived here!"; return; } XmlFsType = new FileSystemType(); switch(magic) { case UFS_MAGIC: sbInformation.AppendLine("UFS filesystem"); XmlFsType.Type = "UFS"; break; case UFS_CIGAM: sbInformation.AppendLine("Big-endian UFS filesystem"); XmlFsType.Type = "UFS"; break; case UFS_MAGIC_BW: sbInformation.AppendLine("BorderWare UFS filesystem"); XmlFsType.Type = "UFS"; break; case UFS_CIGAM_BW: sbInformation.AppendLine("Big-endian BorderWare UFS filesystem"); XmlFsType.Type = "UFS"; break; case UFS2_MAGIC: sbInformation.AppendLine("UFS2 filesystem"); XmlFsType.Type = "UFS2"; break; case UFS2_CIGAM: sbInformation.AppendLine("Big-endian UFS2 filesystem"); XmlFsType.Type = "UFS2"; break; case UFS_BAD_MAGIC: sbInformation.AppendLine("Incompletely initialized UFS filesystem"); sbInformation.AppendLine("BEWARE!!! Following information may be completely wrong!"); XmlFsType.Type = "UFS"; break; case UFS_BAD_CIGAM: sbInformation.AppendLine("Incompletely initialized big-endian UFS filesystem"); sbInformation.AppendLine("BEWARE!!! Following information may be completely wrong!"); XmlFsType.Type = "UFS"; break; } // Fun with seeking follows on superblock reading! ufs_sb_sectors = imagePlugin.ReadSectors(sb_offset, sb_size_in_sectors); IntPtr sbPtr = Marshal.AllocHGlobal(ufs_sb_sectors.Length); Marshal.Copy(ufs_sb_sectors, 0, sbPtr, ufs_sb_sectors.Length); UFSSuperBlock ufs_sb = (UFSSuperBlock)Marshal.PtrToStructure(sbPtr, typeof(UFSSuperBlock)); Marshal.FreeHGlobal(sbPtr); UFSSuperBlock bs_sfu = BigEndianMarshal.ByteArrayToStructureBigEndian(ufs_sb_sectors); if(bs_sfu.fs_magic == UFS_MAGIC && ufs_sb.fs_magic == UFS_CIGAM || bs_sfu.fs_magic == UFS_MAGIC_BW && ufs_sb.fs_magic == UFS_CIGAM_BW || bs_sfu.fs_magic == UFS2_MAGIC && ufs_sb.fs_magic == UFS2_CIGAM || bs_sfu.fs_magic == UFS_BAD_MAGIC && ufs_sb.fs_magic == UFS_BAD_CIGAM) { ufs_sb = bs_sfu; ufs_sb.fs_old_cstotal.cs_nbfree = Swapping.Swap(ufs_sb.fs_old_cstotal.cs_nbfree); ufs_sb.fs_old_cstotal.cs_ndir = Swapping.Swap(ufs_sb.fs_old_cstotal.cs_ndir); ufs_sb.fs_old_cstotal.cs_nffree = Swapping.Swap(ufs_sb.fs_old_cstotal.cs_nffree); ufs_sb.fs_old_cstotal.cs_nifree = Swapping.Swap(ufs_sb.fs_old_cstotal.cs_nifree); ufs_sb.fs_cstotal.cs_numclusters = Swapping.Swap(ufs_sb.fs_cstotal.cs_numclusters); ufs_sb.fs_cstotal.cs_nbfree = Swapping.Swap(ufs_sb.fs_cstotal.cs_nbfree); ufs_sb.fs_cstotal.cs_ndir = Swapping.Swap(ufs_sb.fs_cstotal.cs_ndir); ufs_sb.fs_cstotal.cs_nffree = Swapping.Swap(ufs_sb.fs_cstotal.cs_nffree); ufs_sb.fs_cstotal.cs_nifree = Swapping.Swap(ufs_sb.fs_cstotal.cs_nifree); ufs_sb.fs_cstotal.cs_spare[0] = Swapping.Swap(ufs_sb.fs_cstotal.cs_spare[0]); ufs_sb.fs_cstotal.cs_spare[1] = Swapping.Swap(ufs_sb.fs_cstotal.cs_spare[1]); ufs_sb.fs_cstotal.cs_spare[2] = Swapping.Swap(ufs_sb.fs_cstotal.cs_spare[2]); } DicConsole.DebugWriteLine("FFS plugin", "ufs_sb offset: 0x{0:X8}", sb_offset); DicConsole.DebugWriteLine("FFS plugin", "fs_rlink: 0x{0:X8}", ufs_sb.fs_rlink); DicConsole.DebugWriteLine("FFS plugin", "fs_sblkno: 0x{0:X8}", ufs_sb.fs_sblkno); DicConsole.DebugWriteLine("FFS plugin", "fs_cblkno: 0x{0:X8}", ufs_sb.fs_cblkno); DicConsole.DebugWriteLine("FFS plugin", "fs_iblkno: 0x{0:X8}", ufs_sb.fs_iblkno); DicConsole.DebugWriteLine("FFS plugin", "fs_dblkno: 0x{0:X8}", ufs_sb.fs_dblkno); DicConsole.DebugWriteLine("FFS plugin", "fs_size: 0x{0:X8}", ufs_sb.fs_size); DicConsole.DebugWriteLine("FFS plugin", "fs_dsize: 0x{0:X8}", ufs_sb.fs_dsize); DicConsole.DebugWriteLine("FFS plugin", "fs_ncg: 0x{0:X8}", ufs_sb.fs_ncg); DicConsole.DebugWriteLine("FFS plugin", "fs_bsize: 0x{0:X8}", ufs_sb.fs_bsize); DicConsole.DebugWriteLine("FFS plugin", "fs_fsize: 0x{0:X8}", ufs_sb.fs_fsize); DicConsole.DebugWriteLine("FFS plugin", "fs_frag: 0x{0:X8}", ufs_sb.fs_frag); DicConsole.DebugWriteLine("FFS plugin", "fs_minfree: 0x{0:X8}", ufs_sb.fs_minfree); DicConsole.DebugWriteLine("FFS plugin", "fs_bmask: 0x{0:X8}", ufs_sb.fs_bmask); DicConsole.DebugWriteLine("FFS plugin", "fs_fmask: 0x{0:X8}", ufs_sb.fs_fmask); DicConsole.DebugWriteLine("FFS plugin", "fs_bshift: 0x{0:X8}", ufs_sb.fs_bshift); DicConsole.DebugWriteLine("FFS plugin", "fs_fshift: 0x{0:X8}", ufs_sb.fs_fshift); DicConsole.DebugWriteLine("FFS plugin", "fs_maxcontig: 0x{0:X8}", ufs_sb.fs_maxcontig); DicConsole.DebugWriteLine("FFS plugin", "fs_maxbpg: 0x{0:X8}", ufs_sb.fs_maxbpg); DicConsole.DebugWriteLine("FFS plugin", "fs_fragshift: 0x{0:X8}", ufs_sb.fs_fragshift); DicConsole.DebugWriteLine("FFS plugin", "fs_fsbtodb: 0x{0:X8}", ufs_sb.fs_fsbtodb); DicConsole.DebugWriteLine("FFS plugin", "fs_sbsize: 0x{0:X8}", ufs_sb.fs_sbsize); DicConsole.DebugWriteLine("FFS plugin", "fs_csmask: 0x{0:X8}", ufs_sb.fs_csmask); DicConsole.DebugWriteLine("FFS plugin", "fs_csshift: 0x{0:X8}", ufs_sb.fs_csshift); DicConsole.DebugWriteLine("FFS plugin", "fs_nindir: 0x{0:X8}", ufs_sb.fs_nindir); DicConsole.DebugWriteLine("FFS plugin", "fs_inopb: 0x{0:X8}", ufs_sb.fs_inopb); DicConsole.DebugWriteLine("FFS plugin", "fs_optim: 0x{0:X8}", ufs_sb.fs_optim); DicConsole.DebugWriteLine("FFS plugin", "fs_id_1: 0x{0:X8}", ufs_sb.fs_id_1); DicConsole.DebugWriteLine("FFS plugin", "fs_id_2: 0x{0:X8}", ufs_sb.fs_id_2); DicConsole.DebugWriteLine("FFS plugin", "fs_csaddr: 0x{0:X8}", ufs_sb.fs_csaddr); DicConsole.DebugWriteLine("FFS plugin", "fs_cssize: 0x{0:X8}", ufs_sb.fs_cssize); DicConsole.DebugWriteLine("FFS plugin", "fs_cgsize: 0x{0:X8}", ufs_sb.fs_cgsize); DicConsole.DebugWriteLine("FFS plugin", "fs_ipg: 0x{0:X8}", ufs_sb.fs_ipg); DicConsole.DebugWriteLine("FFS plugin", "fs_fpg: 0x{0:X8}", ufs_sb.fs_fpg); DicConsole.DebugWriteLine("FFS plugin", "fs_fmod: 0x{0:X2}", ufs_sb.fs_fmod); DicConsole.DebugWriteLine("FFS plugin", "fs_clean: 0x{0:X2}", ufs_sb.fs_clean); DicConsole.DebugWriteLine("FFS plugin", "fs_ronly: 0x{0:X2}", ufs_sb.fs_ronly); DicConsole.DebugWriteLine("FFS plugin", "fs_flags: 0x{0:X2}", ufs_sb.fs_flags); DicConsole.DebugWriteLine("FFS plugin", "fs_magic: 0x{0:X8}", ufs_sb.fs_magic); if(ufs_sb.fs_magic == UFS2_MAGIC) fs_type_ufs2 = true; else { const uint SunOSEpoch = 0x1A54C580; // We are supposing there cannot be a Sun's fs created before 1/1/1982 00:00:00 fs_type_43bsd = true; // There is no way of knowing this is the version, but there is of knowing it is not. if(ufs_sb.fs_link > 0) { fs_type_42bsd = true; // It was used in 4.2BSD fs_type_43bsd = false; } if((ufs_sb.fs_maxfilesize & 0xFFFFFFFF) > SunOSEpoch && DateHandlers.UnixUnsignedToDateTime(ufs_sb.fs_maxfilesize & 0xFFFFFFFF) < DateTime.Now) { fs_type_42bsd = false; fs_type_sun = true; fs_type_43bsd = false; } // This is for sure, as it is shared with a sectors/track with non-x86 SunOS, Epoch is absurdly high for that if(ufs_sb.fs_old_npsect > SunOSEpoch && DateHandlers.UnixToDateTime(ufs_sb.fs_old_npsect) < DateTime.Now) { fs_type_42bsd = false; fs_type_sun86 = true; fs_type_sun = false; fs_type_43bsd = false; } if(ufs_sb.fs_cgrotor > 0x00000000 && (uint)ufs_sb.fs_cgrotor < 0xFFFFFFFF) { fs_type_42bsd = false; fs_type_sun = false; fs_type_sun86 = false; fs_type_ufs = true; fs_type_43bsd = false; } // 4.3BSD code does not use these fields, they are always set up to 0 fs_type_43bsd &= ufs_sb.fs_id_2 == 0 && ufs_sb.fs_id_1 == 0; // This is the only 4.4BSD inode format fs_type_44bsd |= ufs_sb.fs_old_inodefmt == 2; } if(!fs_type_ufs2) { sbInformation.AppendLine("There are a lot of variants of UFS using overlapped values on same fields"); sbInformation .AppendLine("I will try to guess which one it is, but unless it's UFS2, I may be surely wrong"); } if(fs_type_42bsd) sbInformation.AppendLine("Guessed as 42BSD FFS"); if(fs_type_43bsd) sbInformation.AppendLine("Guessed as 43BSD FFS"); if(fs_type_44bsd) sbInformation.AppendLine("Guessed as 44BSD FFS"); if(fs_type_sun) sbInformation.AppendLine("Guessed as SunOS FFS"); if(fs_type_sun86) sbInformation.AppendLine("Guessed as SunOS/x86 FFS"); if(fs_type_ufs) sbInformation.AppendLine("Guessed as UFS"); if(fs_type_42bsd) sbInformation.AppendFormat("Linked list of filesystems: 0x{0:X8}", ufs_sb.fs_link).AppendLine(); sbInformation.AppendFormat("Superblock LBA: {0}", ufs_sb.fs_sblkno).AppendLine(); sbInformation.AppendFormat("Cylinder-block LBA: {0}", ufs_sb.fs_cblkno).AppendLine(); sbInformation.AppendFormat("inode-block LBA: {0}", ufs_sb.fs_iblkno).AppendLine(); sbInformation.AppendFormat("First data block LBA: {0}", ufs_sb.fs_dblkno).AppendLine(); sbInformation.AppendFormat("Cylinder group offset in cylinder: {0}", ufs_sb.fs_old_cgoffset).AppendLine(); sbInformation.AppendFormat("Volume last written on {0}", DateHandlers.UnixToDateTime(ufs_sb.fs_old_time)) .AppendLine(); XmlFsType.ModificationDate = DateHandlers.UnixToDateTime(ufs_sb.fs_old_time); XmlFsType.ModificationDateSpecified = true; sbInformation.AppendFormat("{0} blocks in volume ({1} bytes)", ufs_sb.fs_old_size, (long)ufs_sb.fs_old_size * ufs_sb.fs_fsize).AppendLine(); XmlFsType.Clusters = ufs_sb.fs_old_size; XmlFsType.ClusterSize = ufs_sb.fs_fsize; sbInformation.AppendFormat("{0} data blocks in volume ({1} bytes)", ufs_sb.fs_old_dsize, (long)ufs_sb.fs_old_dsize * ufs_sb.fs_fsize).AppendLine(); sbInformation.AppendFormat("{0} cylinder groups in volume", ufs_sb.fs_ncg).AppendLine(); sbInformation.AppendFormat("{0} bytes in a basic block", ufs_sb.fs_bsize).AppendLine(); sbInformation.AppendFormat("{0} bytes in a frag block", ufs_sb.fs_fsize).AppendLine(); sbInformation.AppendFormat("{0} frags in a block", ufs_sb.fs_frag).AppendLine(); sbInformation.AppendFormat("{0}% of blocks must be free", ufs_sb.fs_minfree).AppendLine(); sbInformation.AppendFormat("{0}ms for optimal next block", ufs_sb.fs_old_rotdelay).AppendLine(); sbInformation.AppendFormat("disk rotates {0} times per second ({1}rpm)", ufs_sb.fs_old_rps, ufs_sb.fs_old_rps * 60).AppendLine(); /* sbInformation.AppendFormat("fs_bmask: 0x{0:X8}", ufs_sb.fs_bmask).AppendLine(); sbInformation.AppendFormat("fs_fmask: 0x{0:X8}", ufs_sb.fs_fmask).AppendLine(); sbInformation.AppendFormat("fs_bshift: 0x{0:X8}", ufs_sb.fs_bshift).AppendLine(); sbInformation.AppendFormat("fs_fshift: 0x{0:X8}", ufs_sb.fs_fshift).AppendLine();*/ sbInformation.AppendFormat("{0} contiguous blocks at maximum", ufs_sb.fs_maxcontig).AppendLine(); sbInformation.AppendFormat("{0} blocks per cylinder group at maximum", ufs_sb.fs_maxbpg).AppendLine(); sbInformation.AppendFormat("Superblock is {0} bytes", ufs_sb.fs_sbsize).AppendLine(); sbInformation.AppendFormat("NINDIR: 0x{0:X8}", ufs_sb.fs_nindir).AppendLine(); sbInformation.AppendFormat("INOPB: 0x{0:X8}", ufs_sb.fs_inopb).AppendLine(); sbInformation.AppendFormat("NSPF: 0x{0:X8}", ufs_sb.fs_old_nspf).AppendLine(); switch(ufs_sb.fs_optim) { case 0: sbInformation.AppendLine("Filesystem will minimize allocation time"); break; case 1: sbInformation.AppendLine("Filesystem will minimize volume fragmentation"); break; default: sbInformation.AppendFormat("Unknown optimization value: 0x{0:X8}", ufs_sb.fs_optim).AppendLine(); break; } if(fs_type_sun) sbInformation.AppendFormat("{0} sectors/track", ufs_sb.fs_old_npsect).AppendLine(); else if(fs_type_sun86) sbInformation.AppendFormat("Volume state on {0}", DateHandlers.UnixToDateTime(ufs_sb.fs_old_npsect)) .AppendLine(); sbInformation.AppendFormat("Hardware sector interleave: {0}", ufs_sb.fs_old_interleave).AppendLine(); sbInformation.AppendFormat("Sector 0 skew: {0}/track", ufs_sb.fs_old_trackskew).AppendLine(); if(!fs_type_43bsd && ufs_sb.fs_id_1 > 0 && ufs_sb.fs_id_2 > 0) sbInformation.AppendFormat("Volume ID: 0x{0:X8}{1:X8}", ufs_sb.fs_id_1, ufs_sb.fs_id_2).AppendLine(); else if(fs_type_43bsd && ufs_sb.fs_id_1 > 0 && ufs_sb.fs_id_2 > 0) { sbInformation.AppendFormat("{0} µsec for head switch", ufs_sb.fs_id_1).AppendLine(); sbInformation.AppendFormat("{0} µsec for track-to-track seek", ufs_sb.fs_id_2).AppendLine(); } sbInformation.AppendFormat("Cylinder group summary LBA: {0}", ufs_sb.fs_old_csaddr).AppendLine(); sbInformation.AppendFormat("{0} bytes in cylinder group summary", ufs_sb.fs_cssize).AppendLine(); sbInformation.AppendFormat("{0} bytes in cylinder group", ufs_sb.fs_cgsize).AppendLine(); sbInformation.AppendFormat("{0} tracks/cylinder", ufs_sb.fs_old_ntrak).AppendLine(); sbInformation.AppendFormat("{0} sectors/track", ufs_sb.fs_old_nsect).AppendLine(); sbInformation.AppendFormat("{0} sectors/cylinder", ufs_sb.fs_old_spc).AppendLine(); sbInformation.AppendFormat("{0} cylinder in volume", ufs_sb.fs_old_ncyl).AppendLine(); sbInformation.AppendFormat("{0} cylinders/group", ufs_sb.fs_old_cpg).AppendLine(); sbInformation.AppendFormat("{0} inodes per cylinder group", ufs_sb.fs_ipg).AppendLine(); sbInformation.AppendFormat("{0} blocks per group", ufs_sb.fs_fpg / ufs_sb.fs_frag).AppendLine(); sbInformation.AppendFormat("{0} directories", ufs_sb.fs_old_cstotal.cs_ndir).AppendLine(); sbInformation.AppendFormat("{0} free blocks ({1} bytes)", ufs_sb.fs_old_cstotal.cs_nbfree, (long)ufs_sb.fs_old_cstotal.cs_nbfree * ufs_sb.fs_fsize).AppendLine(); XmlFsType.FreeClusters = ufs_sb.fs_old_cstotal.cs_nbfree; XmlFsType.FreeClustersSpecified = true; sbInformation.AppendFormat("{0} free inodes", ufs_sb.fs_old_cstotal.cs_nifree).AppendLine(); sbInformation.AppendFormat("{0} free frags", ufs_sb.fs_old_cstotal.cs_nffree).AppendLine(); if(ufs_sb.fs_fmod == 1) { sbInformation.AppendLine("Superblock is under modification"); XmlFsType.Dirty = true; } if(ufs_sb.fs_clean == 1) sbInformation.AppendLine("Volume is clean"); if(ufs_sb.fs_ronly == 1) sbInformation.AppendLine("Volume is read-only"); sbInformation.AppendFormat("Volume flags: 0x{0:X2}", ufs_sb.fs_flags).AppendLine(); if(fs_type_ufs) sbInformation.AppendFormat("Volume last mounted on \"{0}\"", StringHandlers.CToString(ufs_sb.fs_fsmnt)) .AppendLine(); else if(fs_type_ufs2) { sbInformation.AppendFormat("Volume last mounted on \"{0}\"", StringHandlers.CToString(ufs_sb.fs_fsmnt)) .AppendLine(); sbInformation.AppendFormat("Volume name: \"{0}\"", StringHandlers.CToString(ufs_sb.fs_volname)) .AppendLine(); XmlFsType.VolumeName = StringHandlers.CToString(ufs_sb.fs_volname); sbInformation.AppendFormat("Volume ID: 0x{0:X16}", ufs_sb.fs_swuid).AppendLine(); //xmlFSType.VolumeSerial = string.Format("{0:X16}", ufs_sb.fs_swuid); sbInformation.AppendFormat("Last searched cylinder group: {0}", ufs_sb.fs_cgrotor).AppendLine(); sbInformation.AppendFormat("{0} contiguously allocated directories", ufs_sb.fs_contigdirs).AppendLine(); sbInformation.AppendFormat("Standard superblock LBA: {0}", ufs_sb.fs_sblkno).AppendLine(); sbInformation.AppendFormat("{0} directories", ufs_sb.fs_cstotal.cs_ndir).AppendLine(); sbInformation.AppendFormat("{0} free blocks ({1} bytes)", ufs_sb.fs_cstotal.cs_nbfree, ufs_sb.fs_cstotal.cs_nbfree * ufs_sb.fs_fsize).AppendLine(); XmlFsType.FreeClusters = ufs_sb.fs_cstotal.cs_nbfree; XmlFsType.FreeClustersSpecified = true; sbInformation.AppendFormat("{0} free inodes", ufs_sb.fs_cstotal.cs_nifree).AppendLine(); sbInformation.AppendFormat("{0} free frags", ufs_sb.fs_cstotal.cs_nffree).AppendLine(); sbInformation.AppendFormat("{0} free clusters", ufs_sb.fs_cstotal.cs_numclusters).AppendLine(); sbInformation.AppendFormat("Volume last written on {0}", DateHandlers.UnixToDateTime(ufs_sb.fs_time)) .AppendLine(); XmlFsType.ModificationDate = DateHandlers.UnixToDateTime(ufs_sb.fs_time); XmlFsType.ModificationDateSpecified = true; sbInformation.AppendFormat("{0} blocks ({1} bytes)", ufs_sb.fs_size, ufs_sb.fs_size * ufs_sb.fs_fsize) .AppendLine(); XmlFsType.Clusters = ufs_sb.fs_size; sbInformation .AppendFormat("{0} data blocks ({1} bytes)", ufs_sb.fs_dsize, ufs_sb.fs_dsize * ufs_sb.fs_fsize) .AppendLine(); sbInformation.AppendFormat("Cylinder group summary area LBA: {0}", ufs_sb.fs_csaddr).AppendLine(); sbInformation.AppendFormat("{0} blocks pending of being freed", ufs_sb.fs_pendingblocks).AppendLine(); sbInformation.AppendFormat("{0} inodes pending of being freed", ufs_sb.fs_pendinginodes).AppendLine(); } if(fs_type_sun) sbInformation.AppendFormat("Volume state on {0}", DateHandlers.UnixToDateTime(ufs_sb.fs_old_npsect)) .AppendLine(); else if(fs_type_sun86) sbInformation.AppendFormat("{0} sectors/track", ufs_sb.fs_state).AppendLine(); else if(fs_type_44bsd) { sbInformation.AppendFormat("{0} blocks on cluster summary array", ufs_sb.fs_contigsumsize).AppendLine(); sbInformation.AppendFormat("Maximum length of a symbolic link: {0}", ufs_sb.fs_maxsymlinklen) .AppendLine(); sbInformation.AppendFormat("A file can be {0} bytes at max", ufs_sb.fs_maxfilesize).AppendLine(); sbInformation.AppendFormat("Volume state on {0}", DateHandlers.UnixToDateTime(ufs_sb.fs_state)) .AppendLine(); } if(ufs_sb.fs_old_nrpos > 0) sbInformation.AppendFormat("{0} rotational positions", ufs_sb.fs_old_nrpos).AppendLine(); if(ufs_sb.fs_old_rotbloff > 0) sbInformation.AppendFormat("{0} blocks per rotation", ufs_sb.fs_old_rotbloff).AppendLine(); information = sbInformation.ToString(); } [StructLayout(LayoutKind.Sequential, Pack = 1)] struct csum { /// number of directories public int cs_ndir; /// number of free blocks public int cs_nbfree; /// number of free inodes public int cs_nifree; /// number of free frags public int cs_nffree; } [StructLayout(LayoutKind.Sequential, Pack = 1)] struct csum_total { /// number of directories public long cs_ndir; /// number of free blocks public long cs_nbfree; /// number of free inodes public long cs_nifree; /// number of free frags public long cs_nffree; /// number of free clusters public long cs_numclusters; /// future expansion [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] public long[] cs_spare; } [StructLayout(LayoutKind.Sequential, Pack = 1)] struct UFSSuperBlock { /// linked list of file systems public uint fs_link; /// /// used for incore super blocks /// on Sun: uint fs_rolled; // logging only: fs fully rolled /// public uint fs_rlink; /// addr of super-block in filesys public int fs_sblkno; /// offset of cyl-block in filesys public int fs_cblkno; /// offset of inode-blocks in filesys public int fs_iblkno; /// offset of first data after cg public int fs_dblkno; /// cylinder group offset in cylinder public int fs_old_cgoffset; /// used to calc mod fs_ntrak public int fs_old_cgmask; /// last time written public int fs_old_time; /// number of blocks in fs public int fs_old_size; /// number of data blocks in fs public int fs_old_dsize; /// number of cylinder groups public int fs_ncg; /// size of basic blocks in fs public int fs_bsize; /// size of frag blocks in fs public int fs_fsize; /// number of frags in a block in fs public int fs_frag; /* these are configuration parameters */ /// minimum percentage of free blocks public int fs_minfree; /// num of ms for optimal next block public int fs_old_rotdelay; /// disk revolutions per second public int fs_old_rps; /* these fields can be computed from the others */ /// ``blkoff'' calc of blk offsets public int fs_bmask; /// ``fragoff'' calc of frag offsets public int fs_fmask; /// ``lblkno'' calc of logical blkno public int fs_bshift; /// ``numfrags'' calc number of frags public int fs_fshift; /* these are configuration parameters */ /// max number of contiguous blks public int fs_maxcontig; /// max number of blks per cyl group public int fs_maxbpg; /* these fields can be computed from the others */ /// block to frag shift public int fs_fragshift; /// fsbtodb and dbtofsb shift constant public int fs_fsbtodb; /// actual size of super block public int fs_sbsize; /// csum block offset public int fs_csmask; /// csum block number public int fs_csshift; /// value of NINDIR public int fs_nindir; /// value of INOPB public uint fs_inopb; /// value of NSPF public int fs_old_nspf; /* yet another configuration parameter */ /// /// optimization preference, see below /// On SVR: int fs_state; // file system state /// public int fs_optim; /// # sectors/track including spares public int fs_old_npsect; /// hardware sector interleave public int fs_old_interleave; /// /// sector 0 skew, per track /// On A/UX: int fs_state; // file system state /// public int fs_old_trackskew; /// /// unique filesystem id /// On old: int fs_headswitch; // head switch time, usec /// public int fs_id_1; /// /// unique filesystem id /// On old: int fs_trkseek; // track-to-track seek, usec /// public int fs_id_2; /* sizes determined by number of cylinder groups and their sizes */ /// blk addr of cyl grp summary area public int fs_old_csaddr; /// size of cyl grp summary area public int fs_cssize; /// cylinder group size public int fs_cgsize; /* these fields are derived from the hardware */ /// tracks per cylinder public int fs_old_ntrak; /// sectors per track public int fs_old_nsect; /// sectors per cylinder public int fs_old_spc; /* this comes from the disk driver partitioning */ /// cylinders in filesystem public int fs_old_ncyl; /* these fields can be computed from the others */ /// cylinders per group public int fs_old_cpg; /// inodes per group public int fs_ipg; /// blocks per group * fs_frag public int fs_fpg; /* this data must be re-computed after crashes */ /// cylinder summary information public csum fs_old_cstotal; /* these fields are cleared at mount time */ /// super block modified flag public sbyte fs_fmod; /// filesystem is clean flag public sbyte fs_clean; /// mounted read-only flag public sbyte fs_ronly; /// old FS_ flags public sbyte fs_old_flags; /// name mounted on [MarshalAs(UnmanagedType.ByValArray, SizeConst = 468)] public byte[] fs_fsmnt; /// volume name [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] public byte[] fs_volname; /// system-wide uid public ulong fs_swuid; /// due to alignment of fs_swuid public int fs_pad; /* these fields retain the current block allocation info */ /// last cg searched public int fs_cgrotor; /// padding; was list of fs_cs buffers [MarshalAs(UnmanagedType.ByValArray, SizeConst = 28)] public uint[] fs_ocsp; /// (u) # of contig. allocated dirs public uint fs_contigdirs; /// (u) cg summary info buffer public uint fs_csp; /// (u) max cluster in each cyl group public uint fs_maxcluster; /// (u) used by snapshots to track fs public uint fs_active; /// cyl per cycle in postbl public int fs_old_cpc; /// maximum blocking factor permitted public int fs_maxbsize; /// number of unreferenced inodes public long fs_unrefs; /// size of underlying GEOM provider public long fs_providersize; /// size of area reserved for metadata public long fs_metaspace; /// old rotation block list head [MarshalAs(UnmanagedType.ByValArray, SizeConst = 14)] public long[] fs_sparecon64; /// byte offset of standard superblock public long fs_sblockloc; /// (u) cylinder summary information public csum_total fs_cstotal; /// last time written public long fs_time; /// number of blocks in fs public long fs_size; /// number of data blocks in fs public long fs_dsize; /// blk addr of cyl grp summary area public long fs_csaddr; /// (u) blocks being freed public long fs_pendingblocks; /// (u) inodes being freed public uint fs_pendinginodes; /// list of snapshot inode numbers [MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)] public uint[] fs_snapinum; /// expected average file size public uint fs_avgfilesize; /// expected # of files per directory public uint fs_avgfpdir; /// save real cg size to use fs_bsize public int fs_save_cgsize; /// Last mount or fsck time. public long fs_mtime; /// SUJ free list public int fs_sujfree; /// reserved for future constants [MarshalAs(UnmanagedType.ByValArray, SizeConst = 23)] public int[] fs_sparecon32; /// see FS_ flags below public int fs_flags; /// size of cluster summary array public int fs_contigsumsize; /// max length of an internal symlink public int fs_maxsymlinklen; /// format of on-disk inodes public int fs_old_inodefmt; /// maximum representable file size public ulong fs_maxfilesize; /// ~fs_bmask for use with 64-bit size public long fs_qbmask; /// ~fs_fmask for use with 64-bit size public long fs_qfmask; /// validate fs_clean field public int fs_state; /// format of positional layout tables public int fs_old_postblformat; /// number of rotational positions public int fs_old_nrpos; /// (short) rotation block list head public int fs_old_postbloff; /// (uchar_t) blocks for each rotation public int fs_old_rotbloff; /// magic number public uint fs_magic; /// list of blocks for each rotation [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)] public byte[] fs_rotbl; } } }