[ext] Fix block traversal.

This commit is contained in:
2026-02-03 21:57:04 +00:00
parent 49350d25b2
commit bce3d25213
5 changed files with 46 additions and 60 deletions

View File

@@ -44,18 +44,15 @@ public sealed partial class extFS
{
blockData = null;
// Calculate block size (1024 << log_zone_size)
uint blockSize = 1024u << (int)_superblock.s_log_zone_size;
// Calculate the byte offset within the partition
ulong byteOffset = (ulong)blockNumber * blockSize;
ulong byteOffset = (ulong)blockNumber * EXT_BLOCK_SIZE;
// Convert to sector address
ulong sectorAddress = byteOffset / _imagePlugin.Info.SectorSize;
var offsetInSector = (int)(byteOffset % _imagePlugin.Info.SectorSize);
// Calculate how many sectors to read
uint sectorsToRead = (blockSize + (uint)offsetInSector + _imagePlugin.Info.SectorSize - 1) /
uint sectorsToRead = (EXT_BLOCK_SIZE + (uint)offsetInSector + _imagePlugin.Info.SectorSize - 1) /
_imagePlugin.Info.SectorSize;
ErrorNumber errno = _imagePlugin.ReadSectors(_partition.Start + sectorAddress,
@@ -66,8 +63,8 @@ public sealed partial class extFS
if(errno != ErrorNumber.NoError) return errno;
blockData = new byte[blockSize];
Array.Copy(sectorData, offsetInSector, blockData, 0, blockSize);
blockData = new byte[EXT_BLOCK_SIZE];
Array.Copy(sectorData, offsetInSector, blockData, 0, EXT_BLOCK_SIZE);
return ErrorNumber.NoError;
}
@@ -88,14 +85,13 @@ public sealed partial class extFS
{
physicalBlock = 0;
uint addressesPerBlock = (1024u << (int)_superblock.s_log_zone_size) / 4; // 4 bytes per block pointer
// Check bounds: max blocks = 9 + 256 + 256*256 + 256*256*256
uint maxBlocks =
9 +
addressesPerBlock +
addressesPerBlock * addressesPerBlock +
addressesPerBlock * addressesPerBlock * addressesPerBlock;
EXT_ADDR_PER_BLOCK +
EXT_ADDR_PER_BLOCK * EXT_ADDR_PER_BLOCK +
EXT_ADDR_PER_BLOCK * EXT_ADDR_PER_BLOCK * EXT_ADDR_PER_BLOCK;
if(logicalBlock >= maxBlocks)
{
@@ -115,7 +111,7 @@ public sealed partial class extFS
uint block = logicalBlock - 9;
// Single indirect block
if(block < addressesPerBlock)
if(block < EXT_ADDR_PER_BLOCK)
{
uint indirectBlock = inode.i_zone[9];
@@ -135,10 +131,10 @@ public sealed partial class extFS
return ErrorNumber.NoError;
}
block -= addressesPerBlock;
block -= EXT_ADDR_PER_BLOCK;
// Double indirect block
if(block < addressesPerBlock * addressesPerBlock)
if(block < EXT_ADDR_PER_BLOCK * EXT_ADDR_PER_BLOCK)
{
uint dindirectBlock = inode.i_zone[10];
@@ -154,7 +150,7 @@ public sealed partial class extFS
if(err != ErrorNumber.NoError) return err;
uint indirectIndex = block / addressesPerBlock;
uint indirectIndex = block / EXT_ADDR_PER_BLOCK;
var indirectAddr = BitConverter.ToUInt32(dindirectData, (int)(indirectIndex * 4));
if(indirectAddr == 0)
@@ -169,13 +165,13 @@ public sealed partial class extFS
if(err != ErrorNumber.NoError) return err;
uint blockIndex = block % addressesPerBlock;
uint blockIndex = block % EXT_ADDR_PER_BLOCK;
physicalBlock = BitConverter.ToUInt32(indirectData2, (int)(blockIndex * 4));
return ErrorNumber.NoError;
}
block -= addressesPerBlock * addressesPerBlock;
block -= EXT_ADDR_PER_BLOCK * EXT_ADDR_PER_BLOCK;
// Triple indirect block
uint tindirectBlock = inode.i_zone[11];
@@ -192,7 +188,7 @@ public sealed partial class extFS
if(errno3 != ErrorNumber.NoError) return errno3;
uint dindirectIndex = block / (addressesPerBlock * addressesPerBlock);
uint dindirectIndex = block / (EXT_ADDR_PER_BLOCK * EXT_ADDR_PER_BLOCK);
var dindirectAddr = BitConverter.ToUInt32(tindirectData, (int)(dindirectIndex * 4));
if(dindirectAddr == 0)
@@ -207,7 +203,7 @@ public sealed partial class extFS
if(errno3 != ErrorNumber.NoError) return errno3;
uint indirectIndex2 = block / addressesPerBlock % addressesPerBlock;
uint indirectIndex2 = block / EXT_ADDR_PER_BLOCK % EXT_ADDR_PER_BLOCK;
var indirectAddr2 = BitConverter.ToUInt32(dindirectData2, (int)(indirectIndex2 * 4));
if(indirectAddr2 == 0)
@@ -222,7 +218,7 @@ public sealed partial class extFS
if(errno3 != ErrorNumber.NoError) return errno3;
uint blockIndex2 = block % addressesPerBlock;
uint blockIndex2 = block % EXT_ADDR_PER_BLOCK;
physicalBlock = BitConverter.ToUInt32(indirectData3, (int)(blockIndex2 * 4));
return ErrorNumber.NoError;

View File

@@ -39,6 +39,12 @@ public sealed partial class extFS
/// <summary>ext superblock magic</summary>
const ushort EXT_MAGIC = 0x137D;
/// <summary>ext block size is always 1024 bytes</summary>
const uint EXT_BLOCK_SIZE = 1024;
/// <summary>Number of addresses per indirect block (1024/4)</summary>
const uint EXT_ADDR_PER_BLOCK = 256;
const int EXT_NAME_LEN = 255;
const int EXT_ROOT_INO = 1;

View File

@@ -33,6 +33,7 @@ using Aaru.CommonTypes.Enums;
using Aaru.CommonTypes.Interfaces;
using Aaru.Helpers;
using Aaru.Logging;
using Marshal = Aaru.Helpers.Marshal;
namespace Aaru.Filesystems;
@@ -182,7 +183,6 @@ public sealed partial class extFS
if(inode.i_size == 0) return ErrorNumber.NoError;
uint blockSize = 1024u << (int)_superblock.s_log_zone_size;
uint bytesRead = 0;
// Process all blocks containing directory data
@@ -197,7 +197,7 @@ public sealed partial class extFS
{
AaruLogging.Debug(MODULE_NAME, "Error mapping block {0}: {1}", blockNum, errno);
blockNum++;
bytesRead += blockSize;
bytesRead += EXT_BLOCK_SIZE;
continue;
}
@@ -206,7 +206,7 @@ public sealed partial class extFS
if(physicalBlock == 0)
{
blockNum++;
bytesRead += blockSize;
bytesRead += EXT_BLOCK_SIZE;
continue;
}
@@ -218,17 +218,17 @@ public sealed partial class extFS
{
AaruLogging.Debug(MODULE_NAME, "Error reading block {0}: {1}", physicalBlock, errno);
blockNum++;
bytesRead += blockSize;
bytesRead += EXT_BLOCK_SIZE;
continue;
}
// Parse directory entries in this block
uint validBytes = Math.Min(blockSize, inode.i_size - bytesRead);
uint validBytes = Math.Min(EXT_BLOCK_SIZE, inode.i_size - bytesRead);
ParseDirectoryBlock(blockData, validBytes, entries);
blockNum++;
bytesRead += blockSize;
bytesRead += EXT_BLOCK_SIZE;
}
return ErrorNumber.NoError;
@@ -240,22 +240,19 @@ public sealed partial class extFS
/// <param name="entries">Dictionary to add entries to</param>
void ParseDirectoryBlock(byte[] blockData, uint validBytes, Dictionary<string, uint> entries)
{
// Minimum directory entry is 8 bytes (inode + rec_len + name_len) + at least 1 byte name
const int minEntrySize = 8;
uint offset = 0;
while(offset < validBytes)
{
// Directory entry structure:
// - 4 bytes: inode number
// - 2 bytes: record length (rec_len)
// - 2 bytes: name length (name_len)
// - N bytes: name
if(offset + 8 > validBytes) // Minimum entry size
if(offset + minEntrySize > validBytes)
break;
var inoNum = BitConverter.ToUInt32(blockData, (int)offset);
var recLen = BitConverter.ToUInt16(blockData, (int)(offset + 4));
var nameLen = BitConverter.ToUInt16(blockData, (int)(offset + 6));
// Read the fixed header fields manually since entries are variable-length on disk
uint inoNum = BitConverter.ToUInt32(blockData, (int)offset);
ushort recLen = BitConverter.ToUInt16(blockData, (int)(offset + 4));
ushort nameLen = BitConverter.ToUInt16(blockData, (int)(offset + 6));
// Validate record length (from Linux kernel validation)
// rec_len must be at least 8 and be a multiple of 8
@@ -289,9 +286,9 @@ public sealed partial class extFS
if(inoNum != 0 && nameLen > 0)
{
// Extract filename
// Extract filename (starts at offset + 8)
var nameBytes = new byte[nameLen];
Array.Copy(blockData, offset + 8, nameBytes, 0, nameLen);
Array.Copy(blockData, (int)(offset + 8), nameBytes, 0, nameLen);
string filename = StringHandlers.CToString(nameBytes, _encoding);
// Skip "." and ".." entries

View File

@@ -31,6 +31,7 @@ using System.Collections.Generic;
using System.Linq;
using Aaru.CommonTypes.Enums;
using Aaru.Logging;
using Marshal = Aaru.Helpers.Marshal;
namespace Aaru.Filesystems;
@@ -39,13 +40,10 @@ namespace Aaru.Filesystems;
public sealed partial class extFS
{
/// <summary>ext inode size in bytes</summary>
const int EXT_INODE_SIZE = 32;
/// <summary>ext block size (always 1024)</summary>
const int EXT_BLOCK_SIZE = 1024;
static readonly int EXT_INODE_SIZE = Marshal.SizeOf<ext_inode>();
/// <summary>Number of inodes per block</summary>
const int EXT_INODES_PER_BLOCK = EXT_BLOCK_SIZE / EXT_INODE_SIZE;
static readonly int EXT_INODES_PER_BLOCK = (int)EXT_BLOCK_SIZE / EXT_INODE_SIZE;
/// <summary>Reads an inode from disk</summary>
/// <param name="inodeNumber">The inode number to read</param>
@@ -65,7 +63,7 @@ public sealed partial class extFS
// Calculate the block containing this inode
// Inode table starts at block 2 (after boot block and superblock)
// From Linux kernel: block = 2 + (ino-1) / EXT_INODES_PER_BLOCK
uint inodeBlock = 2 + (inodeNumber - 1) / EXT_INODES_PER_BLOCK;
uint inodeBlock = (uint)(2 + (inodeNumber - 1) / EXT_INODES_PER_BLOCK);
AaruLogging.Debug(MODULE_NAME,
"Reading inode {0} from block {1} (inodes per block: {2})",
@@ -84,24 +82,12 @@ public sealed partial class extFS
}
// Calculate offset within the block
uint inodeOffset = (inodeNumber - 1) % EXT_INODES_PER_BLOCK * EXT_INODE_SIZE;
int inodeOffset = (int)((inodeNumber - 1) % (uint)EXT_INODES_PER_BLOCK) * EXT_INODE_SIZE;
AaruLogging.Debug(MODULE_NAME, "Inode offset within block: {0}", inodeOffset);
// Parse the inode structure
inode = new ext_inode
{
i_mode = BitConverter.ToUInt16(blockData, (int)inodeOffset),
i_uid = BitConverter.ToUInt16(blockData, (int)(inodeOffset + 2)),
i_size = BitConverter.ToUInt32(blockData, (int)(inodeOffset + 4)),
i_time = BitConverter.ToUInt32(blockData, (int)(inodeOffset + 8)),
i_gid = BitConverter.ToUInt16(blockData, (int)(inodeOffset + 12)),
i_nlinks = BitConverter.ToUInt16(blockData, (int)(inodeOffset + 14)),
i_zone = new uint[12]
};
// Read zone pointers (12 x 4 bytes = 48 bytes starting at offset 16)
for(var i = 0; i < 12; i++) inode.i_zone[i] = BitConverter.ToUInt32(blockData, (int)(inodeOffset + 16 + i * 4));
// Parse the inode structure using marshalling
inode = Marshal.ByteArrayToStructureLittleEndian<ext_inode>(blockData, inodeOffset, EXT_INODE_SIZE);
AaruLogging.Debug(MODULE_NAME,
"Inode {0}: mode=0x{1:X4}, size={2}, nlinks={3}",

View File

@@ -108,8 +108,9 @@ public sealed partial class extFS
public uint next;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct ext_dir_entry {
public uint inode;
public uint inode;
public ushort rec_len;
public ushort name_len;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = EXT_NAME_LEN)]