mirror of
https://github.com/aaru-dps/Aaru.git
synced 2026-07-08 18:16:24 +00:00
[cramfs] Fix handling big-endian inodes.
This commit is contained in:
@@ -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<string, DirectoryEntryInfo>(StringComparer.Ordinal);
|
||||
|
||||
@@ -220,7 +220,7 @@ public sealed partial class Cram
|
||||
: Marshal.ByteArrayToStructureBigEndian<Inode>(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)
|
||||
{
|
||||
|
||||
@@ -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<string, DirectoryEntryInfo>(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<string, DirectoryEntryInfo>(StringComparer.Ordinal);
|
||||
|
||||
|
||||
77
Aaru.Filesystems/Cram/Helpers.cs
Normal file
77
Aaru.Filesystems/Cram/Helpers.cs
Normal file
@@ -0,0 +1,77 @@
|
||||
// /***************************************************************************
|
||||
// Aaru Data Preservation Suite
|
||||
// ----------------------------------------------------------------------------
|
||||
//
|
||||
// Filename : Helpers.cs
|
||||
// Author(s) : Natalia Portillo <claunia@claunia.com>
|
||||
//
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
// ----------------------------------------------------------------------------
|
||||
// Copyright © 2011-2026 Natalia Portillo
|
||||
// ****************************************************************************/
|
||||
|
||||
namespace Aaru.Filesystems;
|
||||
|
||||
public sealed partial class Cram
|
||||
{
|
||||
/// <summary>Gets the file mode from an inode based on current endianness</summary>
|
||||
/// <remarks>
|
||||
/// Little-endian layout: mode in bits 0-15 of modeUid
|
||||
/// Big-endian layout: mode in bits 16-31 of modeUid
|
||||
/// </remarks>
|
||||
ushort GetInodeMode(Inode inode) =>
|
||||
_littleEndian ? (ushort)(inode.modeUid & 0xFFFF) : (ushort)(inode.modeUid >> 16);
|
||||
|
||||
/// <summary>Gets the user ID from an inode based on current endianness</summary>
|
||||
/// <remarks>
|
||||
/// Little-endian layout: uid in bits 16-31 of modeUid
|
||||
/// Big-endian layout: uid in bits 0-15 of modeUid
|
||||
/// </remarks>
|
||||
ushort GetInodeUid(Inode inode) => _littleEndian ? (ushort)(inode.modeUid >> 16) : (ushort)(inode.modeUid & 0xFFFF);
|
||||
|
||||
/// <summary>Gets the file size from an inode based on current endianness</summary>
|
||||
/// <remarks>
|
||||
/// Little-endian layout: size in bits 0-23 of sizeGid
|
||||
/// Big-endian layout: size in bits 8-31 of sizeGid
|
||||
/// </remarks>
|
||||
uint GetInodeSize(Inode inode) => _littleEndian ? inode.sizeGid & 0xFFFFFF : inode.sizeGid >> 8;
|
||||
|
||||
/// <summary>Gets the group ID from an inode based on current endianness</summary>
|
||||
/// <remarks>
|
||||
/// Little-endian layout: gid in bits 24-31 of sizeGid
|
||||
/// Big-endian layout: gid in bits 0-7 of sizeGid
|
||||
/// </remarks>
|
||||
byte GetInodeGid(Inode inode) => _littleEndian ? (byte)(inode.sizeGid >> 24) : (byte)(inode.sizeGid & 0xFF);
|
||||
|
||||
/// <summary>Gets the name length from an inode based on current endianness</summary>
|
||||
/// <remarks>
|
||||
/// Little-endian layout: namelen in bits 0-5 of namelenOffset
|
||||
/// Big-endian layout: namelen in bits 26-31 of namelenOffset
|
||||
/// </remarks>
|
||||
byte GetInodeNameLen(Inode inode) => _littleEndian
|
||||
? (byte)(inode.namelenOffset & 0x3F)
|
||||
: (byte)(inode.namelenOffset >> 26);
|
||||
|
||||
/// <summary>Gets the data offset from an inode based on current endianness</summary>
|
||||
/// <remarks>
|
||||
/// Little-endian layout: offset in bits 6-31 of namelenOffset
|
||||
/// Big-endian layout: offset in bits 0-25 of namelenOffset
|
||||
/// </remarks>
|
||||
uint GetInodeOffset(Inode inode) => _littleEndian ? inode.namelenOffset >> 6 : inode.namelenOffset & 0x3FFFFFF;
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -36,23 +36,28 @@ public sealed partial class Cram
|
||||
/// <summary>Converts a cramfs inode to a FileEntryInfo structure</summary>
|
||||
/// <param name="inode">The cramfs inode</param>
|
||||
/// <returns>FileEntryInfo with the file's metadata</returns>
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -42,44 +42,28 @@ public sealed partial class Cram
|
||||
|
||||
/// <summary>CramFS inode structure</summary>
|
||||
/// <remarks>
|
||||
/// 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)
|
||||
/// </remarks>
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
[SwapEndian]
|
||||
partial struct Inode
|
||||
{
|
||||
/// <summary>Lower 32 bits: mode (16 bits) | uid (16 bits)</summary>
|
||||
/// <summary>Combined mode and uid field</summary>
|
||||
public uint modeUid;
|
||||
|
||||
/// <summary>Middle 32 bits: size (24 bits) | gid (8 bits)</summary>
|
||||
/// <summary>Combined size and gid field</summary>
|
||||
public uint sizeGid;
|
||||
|
||||
/// <summary>Upper 32 bits: namelen (6 bits) | offset (26 bits)</summary>
|
||||
/// <summary>Combined namelen and offset field</summary>
|
||||
public uint namelenOffset;
|
||||
|
||||
/// <summary>Gets the file mode (permissions and type)</summary>
|
||||
public ushort Mode => (ushort)(modeUid & 0xFFFF);
|
||||
|
||||
/// <summary>Gets the user ID</summary>
|
||||
public ushort Uid => (ushort)(modeUid >> 16);
|
||||
|
||||
/// <summary>Gets the file size (or device number for device files)</summary>
|
||||
public uint Size => sizeGid & 0xFFFFFF;
|
||||
|
||||
/// <summary>Gets the group ID</summary>
|
||||
public byte Gid => (byte)(sizeGid >> 24);
|
||||
|
||||
/// <summary>Gets the name length (actual length = namelen * 4)</summary>
|
||||
public byte NameLen => (byte)(namelenOffset & 0x3F);
|
||||
|
||||
/// <summary>Gets the data offset (actual offset = offset * 4)</summary>
|
||||
public uint Offset => namelenOffset >> 6;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
Reference in New Issue
Block a user