// /*************************************************************************** // Aaru Data Preservation Suite // ---------------------------------------------------------------------------- // // Filename : VTOC.cs // Author(s) : Natalia Portillo // // Component : Partitioning scheme plugins. // // --[ Description ] ---------------------------------------------------------- // // Manages UNIX VTOC and disklabels. // // --[ 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-2025 Natalia Portillo // ****************************************************************************/ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Runtime.InteropServices; using Aaru.CommonTypes; using Aaru.CommonTypes.Attributes; using Aaru.CommonTypes.Enums; using Aaru.CommonTypes.Interfaces; using Aaru.Helpers; using Aaru.Logging; using Marshal = Aaru.Helpers.Marshal; namespace Aaru.Partitions; /// /// Implements decoding of UNIX VTOC partitions [SuppressMessage("ReSharper", "UnusedMember.Local")] public sealed partial class VTOC : IPartition { const uint PD_MAGIC = 0xCA5E600D; const uint VTOC_SANE = 0x600DDEEE; const uint PD_CIGAM = 0x0D605ECA; const uint VTOC_ENAS = 0xEEDE0D60; const int V_NUMPAR = 16; const uint XPDVERS = 3; /* 1st version of extended pdinfo */ const string MODULE_NAME = "UNIX VTOC plugin"; #region IPartition Members /// public string Name => Localization.VTOC_Name; /// public Guid Id => new("6D35A66F-8D77-426F-A562-D88F6A1F1702"); /// public string Author => Authors.NATALIA_PORTILLO; /// public bool GetInformation(IMediaImage imagePlugin, out List partitions, ulong sectorOffset) { partitions = []; uint magic = 0; ulong pdloc = 0; byte[] pdsector = null; var magicFound = false; var absolute = false; ErrorNumber errno; foreach(ulong i in new ulong[] { 0, 1, 8, 29 }.TakeWhile(i => i + sectorOffset < imagePlugin.Info.Sectors)) { errno = imagePlugin.ReadSector(i + sectorOffset, false, out pdsector, out _); if(errno != ErrorNumber.NoError) continue; magic = BitConverter.ToUInt32(pdsector, 4); AaruLogging.Debug(MODULE_NAME, Localization.sanity_at_0_is_1_X8_should_be_2_X8_or_3_X8, i + sectorOffset, magic, PD_MAGIC, PD_CIGAM); if(magic != PD_MAGIC && magic != PD_CIGAM) continue; magicFound = true; pdloc = i; break; } if(!magicFound) return false; PDInfo pd; PDInfoOld pdold; if(magic == PD_MAGIC) { pd = Marshal.ByteArrayToStructureLittleEndian(pdsector); pdold = Marshal.ByteArrayToStructureLittleEndian(pdsector); } else { pd = Marshal.ByteArrayToStructureBigEndian(pdsector); pdold = Marshal.ByteArrayToStructureBigEndian(pdsector); } AaruLogging.Debug(MODULE_NAME, "pdinfo.driveid = {0}", pd.driveid); AaruLogging.Debug(MODULE_NAME, "pdinfo.sanity = 0x{0:X8} (should be 0x{1:X8})", pd.sanity, PD_MAGIC); AaruLogging.Debug(MODULE_NAME, "pdinfo.version = {0}", pd.version); AaruLogging.Debug(MODULE_NAME, "pdinfo.serial = \"{0}\"", StringHandlers.CToString(pd.serial)); AaruLogging.Debug(MODULE_NAME, "pdinfo.cyls = {0}", pd.cyls); AaruLogging.Debug(MODULE_NAME, "pdinfo.tracks = {0}", pd.tracks); AaruLogging.Debug(MODULE_NAME, "pdinfo.sectors = {0}", pd.sectors); AaruLogging.Debug(MODULE_NAME, "pdinfo.bytes = {0}", pd.bytes); AaruLogging.Debug(MODULE_NAME, "pdinfo.logicalst = {0}", pd.logicalst); AaruLogging.Debug(MODULE_NAME, "pdinfo.errlogst = {0}", pd.errlogst); AaruLogging.Debug(MODULE_NAME, "pdinfo.errlogsz = {0}", pd.errlogsz); AaruLogging.Debug(MODULE_NAME, "pdinfo.mfgst = {0}", pd.mfgst); AaruLogging.Debug(MODULE_NAME, "pdinfo.mfgsz = {0}", pd.mfgsz); AaruLogging.Debug(MODULE_NAME, "pdinfo.defectst = {0}", pd.defectst); AaruLogging.Debug(MODULE_NAME, "pdinfo.defectsz = {0}", pd.defectsz); AaruLogging.Debug(MODULE_NAME, "pdinfo.relno = {0}", pd.relno); AaruLogging.Debug(MODULE_NAME, "pdinfo.relst = {0}", pd.relst); AaruLogging.Debug(MODULE_NAME, "pdinfo.relsz = {0}", pd.relsz); AaruLogging.Debug(MODULE_NAME, "pdinfo.relnext = {0}", pd.relnext); AaruLogging.Debug(MODULE_NAME, "pdinfo.allcstrt = {0}", pdold.allcstrt); AaruLogging.Debug(MODULE_NAME, "pdinfo.allcend = {0}", pdold.allcend); AaruLogging.Debug(MODULE_NAME, "pdinfo.vtoc_ptr = {0}", pd.vtoc_ptr); AaruLogging.Debug(MODULE_NAME, "pdinfo.vtoc_len = {0}", pd.vtoc_len); AaruLogging.Debug(MODULE_NAME, "pdinfo.vtoc_pad = {0}", pd.vtoc_pad); AaruLogging.Debug(MODULE_NAME, "pdinfo.alt_ptr = {0}", pd.alt_ptr); AaruLogging.Debug(MODULE_NAME, "pdinfo.alt_len = {0}", pd.alt_len); AaruLogging.Debug(MODULE_NAME, "pdinfo.pcyls = {0}", pd.pcyls); AaruLogging.Debug(MODULE_NAME, "pdinfo.ptracks = {0}", pd.ptracks); AaruLogging.Debug(MODULE_NAME, "pdinfo.psectors = {0}", pd.psectors); AaruLogging.Debug(MODULE_NAME, "pdinfo.pbytes = {0}", pd.pbytes); AaruLogging.Debug(MODULE_NAME, "pdinfo.secovhd = {0}", pd.secovhd); AaruLogging.Debug(MODULE_NAME, "pdinfo.interleave = {0}", pd.interleave); AaruLogging.Debug(MODULE_NAME, "pdinfo.skew = {0}", pd.skew); AaruLogging.Debug(MODULE_NAME, "pdinfo.pad[0] = {0}", pd.pad[0]); AaruLogging.Debug(MODULE_NAME, "pdinfo.pad[1] = {0}", pd.pad[1]); AaruLogging.Debug(MODULE_NAME, "pdinfo.pad[2] = {0}", pd.pad[2]); AaruLogging.Debug(MODULE_NAME, "pdinfo.pad[3] = {0}", pd.pad[3]); AaruLogging.Debug(MODULE_NAME, "pdinfo.pad[4] = {0}", pd.pad[4]); AaruLogging.Debug(MODULE_NAME, "pdinfo.pad[5] = {0}", pd.pad[5]); AaruLogging.Debug(MODULE_NAME, "pdinfo.pad[6] = {0}", pd.pad[6]); AaruLogging.Debug(MODULE_NAME, "pdinfo.pad[7] = {0}", pd.pad[7]); magicFound = false; var useOld = false; errno = imagePlugin.ReadSector(pdloc + sectorOffset + 1, false, out byte[] vtocsector, out _); if(errno != ErrorNumber.NoError) return false; var vtoc = new Vtoc(); var vtocOld = new VTocOld(); magic = BitConverter.ToUInt32(vtocsector, 0); if(magic is VTOC_SANE or VTOC_ENAS) { magicFound = true; AaruLogging.Debug(MODULE_NAME, Localization.New_VTOC_found_at_0, pdloc + sectorOffset + 1); if(magic == VTOC_SANE) vtoc = Marshal.ByteArrayToStructureLittleEndian(vtocsector); else { vtoc = Marshal.ByteArrayToStructureBigEndian(vtocsector); for(var i = 0; i < vtoc.v_part.Length; i++) { vtoc.v_part[i].p_tag = (pTag)Swapping.Swap((ushort)vtoc.v_part[i].p_tag); vtoc.v_part[i].p_flag = (pFlag)Swapping.Swap((ushort)vtoc.v_part[i].p_flag); vtoc.v_part[i].p_start = Swapping.Swap(vtoc.v_part[i].p_start); vtoc.v_part[i].p_size = Swapping.Swap(vtoc.v_part[i].p_size); vtoc.timestamp[i] = Swapping.Swap(vtoc.timestamp[i]); } } } if(!magicFound && pd.version < XPDVERS) { magic = BitConverter.ToUInt32(vtocsector, 12); if(magic is VTOC_SANE or VTOC_ENAS) { magicFound = true; useOld = true; AaruLogging.Debug(MODULE_NAME, Localization.Old_VTOC_found_at_0, pdloc + sectorOffset + 1); if(magic == VTOC_SANE) vtocOld = Marshal.ByteArrayToStructureLittleEndian(vtocsector); else { vtocOld = Marshal.ByteArrayToStructureBigEndian(vtocsector); for(var i = 0; i < vtocOld.v_part.Length; i++) { vtocOld.v_part[i].p_tag = (pTag)Swapping.Swap((ushort)vtocOld.v_part[i].p_tag); vtocOld.v_part[i].p_flag = (pFlag)Swapping.Swap((ushort)vtocOld.v_part[i].p_flag); vtocOld.v_part[i].p_start = Swapping.Swap(vtocOld.v_part[i].p_start); vtocOld.v_part[i].p_size = Swapping.Swap(vtocOld.v_part[i].p_size); vtocOld.timestamp[i] = Swapping.Swap(vtocOld.timestamp[i]); } } } } if(!magicFound) { AaruLogging.Debug(MODULE_NAME, Localization.Searching_for_VTOC_on_relative_byte_0, pd.vtoc_ptr); ulong relSecPtr = pd.vtoc_ptr / imagePlugin.Info.SectorSize; uint relSecOff = pd.vtoc_ptr % imagePlugin.Info.SectorSize; uint secCount = (relSecOff + pd.vtoc_len) / imagePlugin.Info.SectorSize; if((relSecOff + pd.vtoc_len) % imagePlugin.Info.SectorSize > 0) secCount++; AaruLogging.Debug(MODULE_NAME, Localization.Going_to_read_0_sectors_from_sector_1_getting_VTOC_from_byte_2, secCount, relSecPtr + sectorOffset, relSecOff); if(relSecPtr + sectorOffset + secCount >= imagePlugin.Info.Sectors) { AaruLogging.Debug(MODULE_NAME, Localization.Going_to_read_past_device_size_aborting); return false; } errno = imagePlugin.ReadSectors(relSecPtr + sectorOffset, false, secCount, out byte[] tmp, out _); if(errno != ErrorNumber.NoError) return false; vtocsector = new byte[pd.vtoc_len]; Array.Copy(tmp, relSecOff, vtocsector, 0, pd.vtoc_len); magic = BitConverter.ToUInt32(vtocsector, 0); if(magic is VTOC_SANE or VTOC_ENAS) { magicFound = true; AaruLogging.Debug(MODULE_NAME, Localization.New_VTOC_found); if(magic == VTOC_SANE) vtoc = Marshal.ByteArrayToStructureLittleEndian(vtocsector); else { vtoc = Marshal.ByteArrayToStructureBigEndian(vtocsector); for(var i = 0; i < vtoc.v_part.Length; i++) { vtoc.v_part[i].p_tag = (pTag)Swapping.Swap((ushort)vtoc.v_part[i].p_tag); vtoc.v_part[i].p_flag = (pFlag)Swapping.Swap((ushort)vtoc.v_part[i].p_flag); vtoc.v_part[i].p_start = Swapping.Swap(vtoc.v_part[i].p_start); vtoc.v_part[i].p_size = Swapping.Swap(vtoc.v_part[i].p_size); vtoc.timestamp[i] = Swapping.Swap(vtoc.timestamp[i]); } } } } if(!magicFound) { AaruLogging.Debug(MODULE_NAME, Localization.Cannot_find_VTOC); return false; } if(useOld) { AaruLogging.Debug(MODULE_NAME, "vtocOld.v_sanity = 0x{0:X8} (should be 0x{1:X8})", vtocOld.v_sanity, VTOC_SANE); AaruLogging.Debug(MODULE_NAME, "vtocOld.v_version = {0}", vtocOld.v_version); AaruLogging.Debug(MODULE_NAME, "vtocOld.v_volume = \"{0}\"", StringHandlers.CToString(vtocOld.v_volume)); AaruLogging.Debug(MODULE_NAME, "vtocOld.v_sectorsz = {0}", vtocOld.v_sectorsz); AaruLogging.Debug(MODULE_NAME, "vtocOld.v_nparts = {0}", vtocOld.v_nparts); for(var i = 0; i < V_NUMPAR; i++) { AaruLogging.Debug(MODULE_NAME, "vtocOld.v_part[{0}].p_tag = {1} ({2})", i, vtocOld.v_part[i].p_tag, (ushort)vtocOld.v_part[i].p_tag); AaruLogging.Debug(MODULE_NAME, "vtocOld.v_part[{0}].p_flag = {1} ({2})", i, vtocOld.v_part[i].p_flag, (ushort)vtocOld.v_part[i].p_flag); AaruLogging.Debug(MODULE_NAME, "vtocOld.v_part[{0}].p_start = {1}", i, vtocOld.v_part[i].p_start); AaruLogging.Debug(MODULE_NAME, "vtocOld.v_part[{0}].p_size = {1}", i, vtocOld.v_part[i].p_size); AaruLogging.Debug(MODULE_NAME, "vtocOld.timestamp[{0}] = {1}", i, DateHandlers.UnixToDateTime(vtocOld.timestamp[i])); } } else { AaruLogging.Debug(MODULE_NAME, "vtoc.v_sanity = 0x{0:X8} (should be 0x{1:X8})", vtoc.v_sanity, VTOC_SANE); AaruLogging.Debug(MODULE_NAME, "vtoc.v_version = {0}", vtoc.v_version); AaruLogging.Debug(MODULE_NAME, "vtoc.v_volume = \"{0}\"", StringHandlers.CToString(vtoc.v_volume)); AaruLogging.Debug(MODULE_NAME, "vtoc.v_pad = {0}", vtoc.v_pad); AaruLogging.Debug(MODULE_NAME, "vtoc.v_nparts = {0}", vtoc.v_nparts); for(var i = 0; i < V_NUMPAR; i++) { AaruLogging.Debug(MODULE_NAME, "vtoc.v_part[{0}].p_tag = {1} ({2})", i, vtoc.v_part[i].p_tag, (ushort)vtoc.v_part[i].p_tag); AaruLogging.Debug(MODULE_NAME, "vtoc.v_part[{0}].p_flag = {1} ({2})", i, vtoc.v_part[i].p_flag, (ushort)vtoc.v_part[i].p_flag); AaruLogging.Debug(MODULE_NAME, "vtoc.v_part[{0}].p_start = {1}", i, vtoc.v_part[i].p_start); AaruLogging.Debug(MODULE_NAME, "vtoc.v_part[{0}].p_size = {1}", i, vtoc.v_part[i].p_size); AaruLogging.Debug(MODULE_NAME, "vtoc.timestamp[{0}] = {1}", i, DateHandlers.UnixToDateTime(vtoc.timestamp[i])); } } uint bps; VtocPartition[] parts; int[] timestamps; if(useOld) { bps = vtocOld.v_sectorsz; parts = vtocOld.v_part; timestamps = vtocOld.timestamp; } else { bps = pd.bytes; parts = vtoc.v_part; timestamps = vtoc.timestamp; } // Check for a partition describing the VTOC whose start is the same as the start we know. // This means partition starts are absolute, not relative, to the VTOC position for(var i = 0; i < V_NUMPAR; i++) { if(parts[i].p_tag != pTag.V_BACKUP || (ulong)parts[i].p_start != sectorOffset) continue; absolute = true; break; } for(var i = 0; i < V_NUMPAR; i++) { if(parts[i].p_tag == pTag.V_UNUSED) continue; var part = new Partition { Start = (ulong)(parts[i].p_start * bps) / imagePlugin.Info.SectorSize, Length = (ulong)(parts[i].p_size * bps) / imagePlugin.Info.SectorSize, Offset = (ulong)(parts[i].p_start * bps), Size = (ulong)(parts[i].p_size * bps), Sequence = (ulong)i, Type = $"UNIX: {DecodeUnixtag(parts[i].p_tag, !useOld)}", Scheme = Name }; var info = ""; // Apparently old ones are absolute :? if(!useOld && !absolute) { part.Start += sectorOffset; part.Offset += sectorOffset * imagePlugin.Info.SectorSize; } if(parts[i].p_flag.HasFlag(pFlag.V_VALID)) info += Localization.valid; if(parts[i].p_flag.HasFlag(pFlag.V_UNMNT)) info += Localization._unmountable_; if(parts[i].p_flag.HasFlag(pFlag.V_OPEN)) info += Localization.open; if(parts[i].p_flag.HasFlag(pFlag.V_REMAP)) info += Localization.alternate_sector_mapping; if(parts[i].p_flag.HasFlag(pFlag.V_RONLY)) info += Localization._read_only_; if(timestamps[i] != 0) info += string.Format(Localization.created_on_0, DateHandlers.UnixToDateTime(timestamps[i])); part.Description = "UNIX slice" + info + "."; if(part.End < imagePlugin.Info.Sectors) partitions.Add(part); } return partitions.Count > 0; } #endregion static string DecodeUnixtag(pTag type, bool isNew) => type switch { pTag.V_UNUSED => Localization.Unused, pTag.V_BOOT => Localization.Boot, pTag.V_ROOT => "/", pTag.V_SWAP => Localization.swap, pTag.V_USER => "/usr", pTag.V_BACKUP => Localization.Whole_disk, pTag.V_STAND_OLD => isNew ? "Stand" : Localization.Alternate_sector_space, pTag.V_VAR_OLD => isNew ? "/var" : Localization.non_UNIX, pTag.V_HOME_OLD => isNew ? "/home" : Localization.Alternate_track_space, pTag.V_ALTSCTR_OLD => isNew ? Localization.Alternate_sector_track : "Stand", pTag.V_CACHE => isNew ? Localization.Cache : "/var", pTag.V_RESERVED => isNew ? Localization.Reserved : "/home", pTag.V_DUMP => Localization.dump, pTag.V_ALTSCTR => Localization.Alternate_sector_track, pTag.V_VMPUBLIC => Localization .volume_mgt_public_partition, pTag.V_VMPRIVATE => Localization .volume_mgt_private_partition, _ => string.Format(Localization.Unknown_TAG_0, type) }; #region Nested type: PDInfo [StructLayout(LayoutKind.Sequential, Pack = 1)] [SwapEndian] // ReSharper disable once InconsistentNaming partial struct PDInfo { public uint driveid; /*identifies the device type*/ public uint sanity; /*verifies device sanity*/ public uint version; /*version number*/ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 12)] public byte[] serial; /*serial number of the device*/ public uint cyls; /*number of cylinders per drive*/ public uint tracks; /*number tracks per cylinder*/ public uint sectors; /*number sectors per track*/ public uint bytes; /*number of bytes per sector*/ public uint logicalst; /*sector address of logical sector 0*/ public uint errlogst; /*sector address of error log area*/ public uint errlogsz; /*size in bytes of error log area*/ public uint mfgst; /*sector address of mfg. defect info*/ public uint mfgsz; /*size in bytes of mfg. defect info*/ public uint defectst; /*sector address of the defect map*/ public uint defectsz; /*size in bytes of defect map*/ public uint relno; /*number of relocation areas*/ public uint relst; /*sector address of relocation area*/ public uint relsz; /*size in sectors of relocation area*/ public uint relnext; /*address of next avail reloc sector*/ /* the previous items are left intact from AT&T's 3b2 pdinfo. Following are added for the 80386 port */ public uint vtoc_ptr; /*byte offset of vtoc block*/ public ushort vtoc_len; /*byte length of vtoc block*/ public ushort vtoc_pad; /* pad for 16-bit machine alignment */ public uint alt_ptr; /*byte offset of alternates table*/ public ushort alt_len; /*byte length of alternates table*/ /* new in version 3 */ public uint pcyls; /*physical cylinders per drive*/ public uint ptracks; /*physical tracks per cylinder*/ public uint psectors; /*physical sectors per track*/ public uint pbytes; /*physical bytes per sector*/ public uint secovhd; /*sector overhead bytes per sector*/ public ushort interleave; /*interleave factor*/ public ushort skew; /*skew factor*/ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] public uint[] pad; /*space for more stuff*/ } #endregion #region Nested type: PDInfoOld [StructLayout(LayoutKind.Sequential, Pack = 1)] [SwapEndian] // ReSharper disable once InconsistentNaming partial struct PDInfoOld { public uint driveid; /*identifies the device type*/ public uint sanity; /*verifies device sanity*/ public uint version; /*version number*/ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 12)] public byte[] serial; /*serial number of the device*/ public uint cyls; /*number of cylinders per drive*/ public uint tracks; /*number tracks per cylinder*/ public uint sectors; /*number sectors per track*/ public uint bytes; /*number of bytes per sector*/ public uint logicalst; /*sector address of logical sector 0*/ public uint errlogst; /*sector address of error log area*/ public uint errlogsz; /*size in bytes of error log area*/ public uint mfgst; /*sector address of mfg. defect info*/ public uint mfgsz; /*size in bytes of mfg. defect info*/ public uint defectst; /*sector address of the defect map*/ public uint defectsz; /*size in bytes of defect map*/ public uint relno; /*number of relocation areas*/ public uint relst; /*sector address of relocation area*/ public uint relsz; /*size in sectors of relocation area*/ public uint relnext; /*address of next avail reloc sector*/ public uint allcstrt; /*start of the allocatable disk*/ public uint allcend; /*end of allocatable disk*/ } #endregion #region Nested type: pFlag [Flags] [SuppressMessage("ReSharper", "InconsistentNaming")] enum pFlag : ushort { /* Partition permission flags */ V_UNMNT = 0x01, /* Unmountable partition */ V_RONLY = 0x10, /* Read only */ V_REMAP = 0x20, /* do alternate sector mapping */ V_OPEN = 0x100, /* Partition open (for driver use) */ V_VALID = 0x200, /* Partition is valid to use */ V_VOMASK = 0x300 /* mask for open and valid */ } #endregion #region Nested type: pTag [SuppressMessage("ReSharper", "InconsistentNaming")] enum pTag : ushort { /// empty V_UNUSED = 0x0000, /// boot V_BOOT = 0x0001, /// root V_ROOT = 0x0002, /// swap V_SWAP = 0x0003, /// /usr V_USER = 0x0004, /// whole disk V_BACKUP = 0x0005, /// stand partition ?? V_STAND_OLD = 0x0006, /// alternate sector space V_ALTS_OLD = 0x0006, /// /var V_VAR_OLD = 0x0007, /// non UNIX V_OTHER = 0x0007, /// /home V_HOME_OLD = 0x0008, /// alternate track space V_ALTS = 0x0008, /// alternate sector track V_ALTSCTR_OLD = 0x0009, /// stand partition ?? V_STAND = 0x0009, /// cache V_CACHE = 0x000A, /// /var V_VAR = 0x000A, /// reserved V_RESERVED = 0x000B, /// /home V_HOME = 0x000B, /// dump partition V_DUMP = 0x000C, /// alternate sector track V_ALTSCTR = 0x000D, /// volume mgt public partition V_VMPUBLIC = 0x000E, /// volume mgt private partition V_VMPRIVATE = 0x000F } #endregion #region Nested type: Vtoc [StructLayout(LayoutKind.Sequential, Pack = 1)] [SwapEndian] // ReSharper disable once InconsistentNaming partial struct Vtoc { public uint v_sanity; /*to verify vtoc sanity*/ public uint v_version; /*layout version*/ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] public byte[] v_volume; /*volume name*/ public ushort v_nparts; /*number of partitions*/ public ushort v_pad; /*pad for 286 compiler*/ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)] public uint[] v_reserved; /*free space*/ [MarshalAs(UnmanagedType.ByValArray, SizeConst = V_NUMPAR)] public VtocPartition[] v_part; /*partition headers*/ [MarshalAs(UnmanagedType.ByValArray, SizeConst = V_NUMPAR)] public int[] timestamp; /* SCSI time stamp */ } #endregion #region Nested type: VTocOld [StructLayout(LayoutKind.Sequential, Pack = 1)] [SwapEndian] // ReSharper disable once InconsistentNaming partial struct VTocOld { [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] public uint[] v_bootinfo; /*info needed by mboot*/ public uint v_sanity; /*to verify vtoc sanity*/ public uint v_version; /*layout version*/ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] public byte[] v_volume; /*volume name*/ public ushort v_sectorsz; /*sector size in bytes*/ public ushort v_nparts; /*number of partitions*/ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)] public uint[] v_reserved; /*free space*/ [MarshalAs(UnmanagedType.ByValArray, SizeConst = V_NUMPAR)] public VtocPartition[] v_part; /*partition headers*/ [MarshalAs(UnmanagedType.ByValArray, SizeConst = V_NUMPAR)] public int[] timestamp; /* SCSI time stamp */ } #endregion #region Nested type: VtocPartition [StructLayout(LayoutKind.Sequential, Pack = 1)] [SwapEndian] // ReSharper disable once InconsistentNaming partial struct VtocPartition { public pTag p_tag; /*ID tag of partition*/ public pFlag p_flag; /*permision flags*/ public int p_start; /*start sector no of partition*/ public int p_size; /*# of blocks in partition*/ } #endregion }