diff --git a/Aaru.Filesystems/Cram/Dir.cs b/Aaru.Filesystems/Cram/Dir.cs index 34360d15b..e34770b12 100644 --- a/Aaru.Filesystems/Cram/Dir.cs +++ b/Aaru.Filesystems/Cram/Dir.cs @@ -102,19 +102,19 @@ public sealed partial class Cram AaruLogging.Debug(MODULE_NAME, "OpenDir: Component '{0}' found", component); // Check if it's a directory - if(!IsDirectory(entry.Inode.Mode)) + if(!IsDirectory(GetInodeMode(entry.Inode))) { AaruLogging.Debug(MODULE_NAME, "OpenDir: '{0}' is not a directory (mode=0x{1:X4})", component, - entry.Inode.Mode); + GetInodeMode(entry.Inode)); return ErrorNumber.NotDirectory; } // Read directory contents - uint dirOffset = entry.Inode.Offset << 2; - uint dirSize = entry.Inode.Size; + uint dirOffset = GetInodeOffset(entry.Inode) << 2; + uint dirSize = GetInodeSize(entry.Inode); var dirEntries = new Dictionary(StringComparer.Ordinal); @@ -220,7 +220,7 @@ public sealed partial class Cram : Marshal.ByteArrayToStructureBigEndian(inodeData); // Name length is stored as (actual_length + 3) / 4, so multiply by 4 to get padded length - int nameLen = inode.NameLen << 2; + int nameLen = GetInodeNameLen(inode) << 2; if(nameLen == 0) { diff --git a/Aaru.Filesystems/Cram/File.cs b/Aaru.Filesystems/Cram/File.cs index ea63b907a..604b2aabf 100644 --- a/Aaru.Filesystems/Cram/File.cs +++ b/Aaru.Filesystems/Cram/File.cs @@ -98,7 +98,7 @@ public sealed partial class Cram } // Not the last component - must be a directory - if(!IsDirectory(entry.Inode.Mode)) + if(!IsDirectory(GetInodeMode(entry.Inode))) { AaruLogging.Debug(MODULE_NAME, "Stat: '{0}' is not a directory", component); @@ -106,8 +106,8 @@ public sealed partial class Cram } // Read directory contents for next iteration - uint dirOffset = entry.Inode.Offset << 2; - uint dirSize = entry.Inode.Size; + uint dirOffset = GetInodeOffset(entry.Inode) << 2; + uint dirSize = GetInodeSize(entry.Inode); var dirEntries = new Dictionary(StringComparer.Ordinal); @@ -165,7 +165,7 @@ public sealed partial class Cram return errno; } - uint size = entry.Inode.Size; + uint size = GetInodeSize(entry.Inode); uint blockCount = (size + PAGE_SIZE - 1) / PAGE_SIZE; node = new CramFileNode @@ -174,7 +174,7 @@ public sealed partial class Cram Length = size, Offset = 0, Inode = entry.Inode, - BlockPtrOffset = entry.Inode.Offset << 2, + BlockPtrOffset = GetInodeOffset(entry.Inode) << 2, BlockCount = blockCount }; @@ -219,7 +219,7 @@ public sealed partial class Cram if(errno != ErrorNumber.NoError) return errno; - uint size = entry.Inode.Size; + uint size = GetInodeSize(entry.Inode); if(size == 0) { @@ -238,7 +238,7 @@ public sealed partial class Cram Length = size, Offset = 0, Inode = entry.Inode, - BlockPtrOffset = entry.Inode.Offset << 2, + BlockPtrOffset = GetInodeOffset(entry.Inode) << 2, BlockCount = blockCount }; @@ -381,11 +381,11 @@ public sealed partial class Cram } // Not the last component - must be a directory - if(!IsDirectory(foundEntry.Inode.Mode)) return ErrorNumber.NotDirectory; + if(!IsDirectory(GetInodeMode(foundEntry.Inode))) return ErrorNumber.NotDirectory; // Read directory contents for next iteration - uint dirOffset = foundEntry.Inode.Offset << 2; - uint dirSize = foundEntry.Inode.Size; + uint dirOffset = GetInodeOffset(foundEntry.Inode) << 2; + uint dirSize = GetInodeSize(foundEntry.Inode); var dirEntries = new Dictionary(StringComparer.Ordinal); diff --git a/Aaru.Filesystems/Cram/Helpers.cs b/Aaru.Filesystems/Cram/Helpers.cs new file mode 100644 index 000000000..635f76848 --- /dev/null +++ b/Aaru.Filesystems/Cram/Helpers.cs @@ -0,0 +1,77 @@ +// /*************************************************************************** +// Aaru Data Preservation Suite +// ---------------------------------------------------------------------------- +// +// Filename : Helpers.cs +// Author(s) : Natalia Portillo +// +// Component : Cram file system plugin. +// +// --[ 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-2026 Natalia Portillo +// ****************************************************************************/ + +namespace Aaru.Filesystems; + +public sealed partial class Cram +{ + /// Gets the file mode from an inode based on current endianness + /// + /// Little-endian layout: mode in bits 0-15 of modeUid + /// Big-endian layout: mode in bits 16-31 of modeUid + /// + ushort GetInodeMode(Inode inode) => + _littleEndian ? (ushort)(inode.modeUid & 0xFFFF) : (ushort)(inode.modeUid >> 16); + + /// Gets the user ID from an inode based on current endianness + /// + /// Little-endian layout: uid in bits 16-31 of modeUid + /// Big-endian layout: uid in bits 0-15 of modeUid + /// + ushort GetInodeUid(Inode inode) => _littleEndian ? (ushort)(inode.modeUid >> 16) : (ushort)(inode.modeUid & 0xFFFF); + + /// Gets the file size from an inode based on current endianness + /// + /// Little-endian layout: size in bits 0-23 of sizeGid + /// Big-endian layout: size in bits 8-31 of sizeGid + /// + uint GetInodeSize(Inode inode) => _littleEndian ? inode.sizeGid & 0xFFFFFF : inode.sizeGid >> 8; + + /// Gets the group ID from an inode based on current endianness + /// + /// Little-endian layout: gid in bits 24-31 of sizeGid + /// Big-endian layout: gid in bits 0-7 of sizeGid + /// + byte GetInodeGid(Inode inode) => _littleEndian ? (byte)(inode.sizeGid >> 24) : (byte)(inode.sizeGid & 0xFF); + + /// Gets the name length from an inode based on current endianness + /// + /// Little-endian layout: namelen in bits 0-5 of namelenOffset + /// Big-endian layout: namelen in bits 26-31 of namelenOffset + /// + byte GetInodeNameLen(Inode inode) => _littleEndian + ? (byte)(inode.namelenOffset & 0x3F) + : (byte)(inode.namelenOffset >> 26); + + /// Gets the data offset from an inode based on current endianness + /// + /// Little-endian layout: offset in bits 6-31 of namelenOffset + /// Big-endian layout: offset in bits 0-25 of namelenOffset + /// + uint GetInodeOffset(Inode inode) => _littleEndian ? inode.namelenOffset >> 6 : inode.namelenOffset & 0x3FFFFFF; +} \ No newline at end of file diff --git a/Aaru.Filesystems/Cram/Info.cs b/Aaru.Filesystems/Cram/Info.cs index 296839252..9fb02c365 100644 --- a/Aaru.Filesystems/Cram/Info.cs +++ b/Aaru.Filesystems/Cram/Info.cs @@ -124,10 +124,14 @@ public sealed partial class Cram } // Display root inode information - sbInformation.AppendFormat(Localization.Cram_Root_directory_mode_0, Convert.ToString(crSb.root.Mode, 8)) - .AppendLine(); + // Bit extraction depends on endianness - LE: mode in low bits, BE: mode in high bits + ushort rootMode = littleEndian ? (ushort)(crSb.root.modeUid & 0xFFFF) : (ushort)(crSb.root.modeUid >> 16); - sbInformation.AppendFormat(Localization.Cram_Root_directory_size_0_bytes, crSb.root.Size).AppendLine(); + uint rootSize = littleEndian ? crSb.root.sizeGid & 0xFFFFFF : crSb.root.sizeGid >> 8; + + sbInformation.AppendFormat(Localization.Cram_Root_directory_mode_0, Convert.ToString(rootMode, 8)).AppendLine(); + + sbInformation.AppendFormat(Localization.Cram_Root_directory_size_0_bytes, rootSize).AppendLine(); information = sbInformation.ToString(); diff --git a/Aaru.Filesystems/Cram/Inode.cs b/Aaru.Filesystems/Cram/Inode.cs index e74e98ac2..097417ac6 100644 --- a/Aaru.Filesystems/Cram/Inode.cs +++ b/Aaru.Filesystems/Cram/Inode.cs @@ -36,23 +36,28 @@ public sealed partial class Cram /// Converts a cramfs inode to a FileEntryInfo structure /// The cramfs inode /// FileEntryInfo with the file's metadata - static FileEntryInfo InodeToFileEntryInfo(Inode inode) + FileEntryInfo InodeToFileEntryInfo(Inode inode) { + ushort mode = GetInodeMode(inode); + ushort uid = GetInodeUid(inode); + uint size = GetInodeSize(inode); + byte gid = GetInodeGid(inode); + var info = new FileEntryInfo { Attributes = FileAttributes.None, BlockSize = 4096, // CramFS uses 4K pages - Length = inode.Size, - UID = inode.Uid, - GID = inode.Gid, - Mode = (uint)(inode.Mode & S_IPERM) + Length = size, + UID = uid, + GID = gid, + Mode = (uint)(mode & S_IPERM) }; // CramFS doesn't store timestamps // Links count is not stored in cramfs // Determine file type from mode - var fileType = (ushort)(inode.Mode & S_IFMT); + var fileType = (ushort)(mode & S_IFMT); switch(fileType) { @@ -73,9 +78,8 @@ public sealed partial class Cram // For device files, Size field contains device number (i_rdev) // Major = bits 8-15, Minor = bits 0-7 - uint chrDev = inode.Size; - uint chrMajor = chrDev >> 8 & 0xFF; - uint chrMinor = chrDev & 0xFF; + uint chrMajor = size >> 8 & 0xFF; + uint chrMinor = size & 0xFF; info.DeviceNo = (ulong)chrMajor << 32 | chrMinor; break; @@ -84,9 +88,8 @@ public sealed partial class Cram // For device files, Size field contains device number (i_rdev) // Major = bits 8-15, Minor = bits 0-7 - uint blkDev = inode.Size; - uint blkMajor = blkDev >> 8 & 0xFF; - uint blkMinor = blkDev & 0xFF; + uint blkMajor = size >> 8 & 0xFF; + uint blkMinor = size & 0xFF; info.DeviceNo = (ulong)blkMajor << 32 | blkMinor; break; @@ -105,7 +108,7 @@ public sealed partial class Cram } // Calculate blocks (size / block size, rounded up) - info.Blocks = (inode.Size + 4095) / 4096; + info.Blocks = (size + 4095) / 4096; return info; } diff --git a/Aaru.Filesystems/Cram/Mount.cs b/Aaru.Filesystems/Cram/Mount.cs index 64bc5612f..9324dd0cc 100644 --- a/Aaru.Filesystems/Cram/Mount.cs +++ b/Aaru.Filesystems/Cram/Mount.cs @@ -75,9 +75,9 @@ public sealed partial class Cram } // Validate root inode is a directory - if(!IsDirectory(_superBlock.root.Mode)) + if(!IsDirectory(GetInodeMode(_superBlock.root))) { - AaruLogging.Debug(MODULE_NAME, "Root is not a directory, mode: 0x{0:X4}", _superBlock.root.Mode); + AaruLogging.Debug(MODULE_NAME, "Root is not a directory, mode: 0x{0:X4}", GetInodeMode(_superBlock.root)); return ErrorNumber.InvalidArgument; } @@ -232,8 +232,8 @@ public sealed partial class Cram ErrorNumber LoadRootDirectory() { // Root directory offset is in the root inode - uint rootOffset = _superBlock.root.Offset << 2; - uint rootSize = _superBlock.root.Size; + uint rootOffset = GetInodeOffset(_superBlock.root) << 2; + uint rootSize = GetInodeSize(_superBlock.root); AaruLogging.Debug(MODULE_NAME, "Root directory offset: {0}, size: {1}", rootOffset, rootSize); diff --git a/Aaru.Filesystems/Cram/Structs.cs b/Aaru.Filesystems/Cram/Structs.cs index a667f0a0e..5ca5eef9f 100644 --- a/Aaru.Filesystems/Cram/Structs.cs +++ b/Aaru.Filesystems/Cram/Structs.cs @@ -42,44 +42,28 @@ public sealed partial class Cram /// CramFS inode structure /// - /// The inode uses bit fields: - /// - mode: 16 bits (file mode/permissions) - /// - uid: 16 bits (user ID) - /// - size: 24 bits (file size, or i_rdev for device files) - /// - gid: 8 bits (group ID) - /// - namelen: 6 bits (name length divided by 4, rounded up) - /// - offset: 26 bits (data offset divided by 4) + /// The inode uses bit fields that are laid out differently on big-endian vs little-endian systems: + /// Little-endian layout: + /// - modeUid: mode (16 bits) | uid (16 bits) + /// - sizeGid: size (24 bits) | gid (8 bits) + /// - namelenOffset: namelen (6 bits) | offset (26 bits) + /// Big-endian layout: + /// - modeUid: uid (16 bits) | mode (16 bits) + /// - sizeGid: gid (8 bits) | size (24 bits) + /// - namelenOffset: offset (26 bits) | namelen (6 bits) /// [StructLayout(LayoutKind.Sequential, Pack = 1)] [SwapEndian] partial struct Inode { - /// Lower 32 bits: mode (16 bits) | uid (16 bits) + /// Combined mode and uid field public uint modeUid; - /// Middle 32 bits: size (24 bits) | gid (8 bits) + /// Combined size and gid field public uint sizeGid; - /// Upper 32 bits: namelen (6 bits) | offset (26 bits) + /// Combined namelen and offset field public uint namelenOffset; - - /// Gets the file mode (permissions and type) - public ushort Mode => (ushort)(modeUid & 0xFFFF); - - /// Gets the user ID - public ushort Uid => (ushort)(modeUid >> 16); - - /// Gets the file size (or device number for device files) - public uint Size => sizeGid & 0xFFFFFF; - - /// Gets the group ID - public byte Gid => (byte)(sizeGid >> 24); - - /// Gets the name length (actual length = namelen * 4) - public byte NameLen => (byte)(namelenOffset & 0x3F); - - /// Gets the data offset (actual offset = offset * 4) - public uint Offset => namelenOffset >> 6; } #endregion