mirror of
https://github.com/aaru-dps/Aaru.git
synced 2026-02-04 00:44:39 +00:00
[ext] Fix block traversal.
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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}",
|
||||
|
||||
@@ -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)]
|
||||
|
||||
Reference in New Issue
Block a user