From ef35a821f1e44771ffb3c1cbaafe8c38cc2867fd Mon Sep 17 00:00:00 2001 From: Nathaniel van Diepen Date: Thu, 11 Dec 2025 17:54:30 -0700 Subject: [PATCH 1/9] Switch to crc32c --- ext4/blockdescriptor.py | 2 +- ext4/extent.py | 2 +- ext4/inode.py | 15 +++++++++------ ext4/struct.py | 3 --- ext4/superblock.py | 4 ++-- ext4/xattr.py | 2 +- requirements.txt | 2 +- 7 files changed, 15 insertions(+), 15 deletions(-) diff --git a/ext4/blockdescriptor.py b/ext4/blockdescriptor.py index 363d214..344e169 100644 --- a/ext4/blockdescriptor.py +++ b/ext4/blockdescriptor.py @@ -1,9 +1,9 @@ from ctypes import c_uint32 from ctypes import c_uint16 +from crc32c import crc32c from .enum import EXT4_BG from .struct import Ext4Struct -from .struct import crc32c class BlockDescriptor(Ext4Struct): diff --git a/ext4/extent.py b/ext4/extent.py index 13929ea..7330ba8 100644 --- a/ext4/extent.py +++ b/ext4/extent.py @@ -1,9 +1,9 @@ from ctypes import c_uint32 from ctypes import c_uint16 from ctypes import sizeof +from crc32c import crc32c from .struct import Ext4Struct -from .struct import crc32c class ExtentBlocks(object): diff --git a/ext4/inode.py b/ext4/inode.py index d07e056..c84b4e4 100644 --- a/ext4/inode.py +++ b/ext4/inode.py @@ -8,11 +8,11 @@ from ctypes import c_uint32 from ctypes import c_uint16 from ctypes import sizeof +from crc32c import crc32c from ._compat import override from .struct import Ext4Struct -from .struct import crc32c from .struct import MagicError from .enum import EXT4_OS @@ -244,11 +244,14 @@ def checksum(self): if self.has_hi: offset = Inode.i_checksum_hi.offset csum = crc32c(data[self.EXT2_GOOD_OLD_INODE_SIZE : offset], csum) - if self.fits_in_hi: - csum = crc32c(b"\0" * Inode.i_checksum_hi.size, csum) - offset += Inode.i_checksum_hi.size - - csum = crc32c(data[offset:], csum) + csum = crc32c(b"\0" * Inode.i_checksum_hi.size, csum) + csum = crc32c( + data[ + offset + Inode.i_checksum_hi.size : self.EXT2_GOOD_OLD_INODE_SIZE + + self.i_extra_isize + ], + csum, + ) if not self.has_hi: csum &= 0xFFFF diff --git a/ext4/struct.py b/ext4/struct.py index fe2bf39..d294fcc 100644 --- a/ext4/struct.py +++ b/ext4/struct.py @@ -4,9 +4,6 @@ from ctypes import memmove from ctypes import addressof from ctypes import sizeof -from crcmod import mkCrcFun - -crc32c = mkCrcFun(0x11EDC6F41) class MagicError(Exception): diff --git a/ext4/superblock.py b/ext4/superblock.py index b4e3bd6..7881f4a 100644 --- a/ext4/superblock.py +++ b/ext4/superblock.py @@ -3,6 +3,7 @@ from ctypes import c_uint16 from ctypes import c_uint8 from ctypes import c_ubyte +from crc32c import crc32c from .enum import EXT4_FS from .enum import EXT4_ERRORS @@ -18,7 +19,6 @@ from .enum import EXT4_MOUNT from .enum import FS_ENCRYPTION_MODE from .struct import Ext4Struct -from .struct import crc32c class Superblock(Ext4Struct): @@ -183,7 +183,7 @@ def seed(self): if self.s_feature_incompat & EXT4_FEATURE_INCOMPAT.CSUM_SEED != 0: return self.s_checksum_seed - return crc32c(bytes(self.s_uuid)) + return crc32c(bytes(self.s_uuid), 0xFFFFFFFF) @property def desc_size(self): diff --git a/ext4/xattr.py b/ext4/xattr.py index 154de7a..1d74076 100644 --- a/ext4/xattr.py +++ b/ext4/xattr.py @@ -4,9 +4,9 @@ from ctypes import c_uint16 from ctypes import c_uint8 from ctypes import sizeof +from crc32c import crc32c from .struct import Ext4Struct -from .struct import crc32c from .enum import EXT4_FL from .enum import EXT4_FEATURE_INCOMPAT from ._compat import override diff --git a/requirements.txt b/requirements.txt index 0e67294..0176b96 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,2 @@ cachetools==6.0.0 -crcmod==1.7 +crc32c==2.8 From ad0f55943ab549599189e739a28e2c543fa02af1 Mon Sep 17 00:00:00 2001 From: Nathaniel van Diepen Date: Thu, 11 Dec 2025 17:56:04 -0700 Subject: [PATCH 2/9] Bump version --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index d42c885..b22d631 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "ext4" -version = "1.2.1" +version = "1.2.2" authors = [ { name="Eeems", email="eeems@eeems.email" }, ] From e6700e7aaad2b51ee532cae6787e80849a4b4999 Mon Sep 17 00:00:00 2001 From: Nathaniel van Diepen Date: Thu, 11 Dec 2025 18:17:36 -0700 Subject: [PATCH 3/9] Add missing offset, fix checksum logic --- ext4/inode.py | 10 +++++----- ext4/superblock.py | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/ext4/inode.py b/ext4/inode.py index c84b4e4..cd7d972 100644 --- a/ext4/inode.py +++ b/ext4/inode.py @@ -244,12 +244,12 @@ def checksum(self): if self.has_hi: offset = Inode.i_checksum_hi.offset csum = crc32c(data[self.EXT2_GOOD_OLD_INODE_SIZE : offset], csum) - csum = crc32c(b"\0" * Inode.i_checksum_hi.size, csum) + if self.fits_in_hi: + csum = crc32c(b"\0" * Inode.i_checksum_hi.size, csum) + offset += Inode.i_checksum_hi.size + csum = crc32c( - data[ - offset + Inode.i_checksum_hi.size : self.EXT2_GOOD_OLD_INODE_SIZE - + self.i_extra_isize - ], + data[offset : self.EXT2_GOOD_OLD_INODE_SIZE + self.i_extra_isize], csum, ) diff --git a/ext4/superblock.py b/ext4/superblock.py index 7881f4a..4412b03 100644 --- a/ext4/superblock.py +++ b/ext4/superblock.py @@ -173,7 +173,7 @@ def expected_checksum(self): @property def checksum(self): return ( - crc32c(bytes(self)[: Superblock.s_checksum.offset]) + crc32c(bytes(self)[: Superblock.s_checksum.offset], 0xFFFFFFFF) if self.metadata_csum else None ) From 9bc88422ac43e93a6d2827d161ec57a242e6406b Mon Sep 17 00:00:00 2001 From: Nathaniel van Diepen Date: Thu, 11 Dec 2025 18:25:33 -0700 Subject: [PATCH 4/9] Switch back to crcmod --- ext4/blockdescriptor.py | 2 +- ext4/extent.py | 2 +- ext4/inode.py | 2 +- ext4/struct.py | 3 +++ ext4/superblock.py | 2 +- ext4/xattr.py | 2 +- requirements.txt | 2 +- 7 files changed, 9 insertions(+), 6 deletions(-) diff --git a/ext4/blockdescriptor.py b/ext4/blockdescriptor.py index 344e169..363d214 100644 --- a/ext4/blockdescriptor.py +++ b/ext4/blockdescriptor.py @@ -1,9 +1,9 @@ from ctypes import c_uint32 from ctypes import c_uint16 -from crc32c import crc32c from .enum import EXT4_BG from .struct import Ext4Struct +from .struct import crc32c class BlockDescriptor(Ext4Struct): diff --git a/ext4/extent.py b/ext4/extent.py index 7330ba8..579bec4 100644 --- a/ext4/extent.py +++ b/ext4/extent.py @@ -1,8 +1,8 @@ from ctypes import c_uint32 from ctypes import c_uint16 from ctypes import sizeof -from crc32c import crc32c +from .struct import crc32c from .struct import Ext4Struct diff --git a/ext4/inode.py b/ext4/inode.py index cd7d972..c383ae0 100644 --- a/ext4/inode.py +++ b/ext4/inode.py @@ -8,12 +8,12 @@ from ctypes import c_uint32 from ctypes import c_uint16 from ctypes import sizeof -from crc32c import crc32c from ._compat import override from .struct import Ext4Struct from .struct import MagicError +from .struct import crc32c from .enum import EXT4_OS from .enum import EXT4_FL diff --git a/ext4/struct.py b/ext4/struct.py index d294fcc..fe2bf39 100644 --- a/ext4/struct.py +++ b/ext4/struct.py @@ -4,6 +4,9 @@ from ctypes import memmove from ctypes import addressof from ctypes import sizeof +from crcmod import mkCrcFun + +crc32c = mkCrcFun(0x11EDC6F41) class MagicError(Exception): diff --git a/ext4/superblock.py b/ext4/superblock.py index 4412b03..abf5f42 100644 --- a/ext4/superblock.py +++ b/ext4/superblock.py @@ -3,7 +3,6 @@ from ctypes import c_uint16 from ctypes import c_uint8 from ctypes import c_ubyte -from crc32c import crc32c from .enum import EXT4_FS from .enum import EXT4_ERRORS @@ -19,6 +18,7 @@ from .enum import EXT4_MOUNT from .enum import FS_ENCRYPTION_MODE from .struct import Ext4Struct +from .struct import crc32c class Superblock(Ext4Struct): diff --git a/ext4/xattr.py b/ext4/xattr.py index 1d74076..8c9afef 100644 --- a/ext4/xattr.py +++ b/ext4/xattr.py @@ -4,11 +4,11 @@ from ctypes import c_uint16 from ctypes import c_uint8 from ctypes import sizeof -from crc32c import crc32c from .struct import Ext4Struct from .enum import EXT4_FL from .enum import EXT4_FEATURE_INCOMPAT +from .struct import crc32c from ._compat import override diff --git a/requirements.txt b/requirements.txt index 0176b96..0e67294 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,2 @@ cachetools==6.0.0 -crc32c==2.8 +crcmod==1.7 From 94f019efeea271a3bc5f17199ce2aeac4aada8e0 Mon Sep 17 00:00:00 2001 From: Nathaniel van Diepen Date: Thu, 11 Dec 2025 18:27:45 -0700 Subject: [PATCH 5/9] Clamp --- ext4/inode.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/ext4/inode.py b/ext4/inode.py index c383ae0..a332e2f 100644 --- a/ext4/inode.py +++ b/ext4/inode.py @@ -249,7 +249,13 @@ def checksum(self): offset += Inode.i_checksum_hi.size csum = crc32c( - data[offset : self.EXT2_GOOD_OLD_INODE_SIZE + self.i_extra_isize], + data[ + offset : self.EXT2_GOOD_OLD_INODE_SIZE + + min( + self.i_extra_isize, + self.superblock.s_inode_size - self.EXT2_GOOD_OLD_INODE_SIZE, + ) + ], csum, ) From de7b9280a807c7845937e68f3da36b866720f5c0 Mon Sep 17 00:00:00 2001 From: Nathaniel van Diepen Date: Thu, 11 Dec 2025 18:46:31 -0700 Subject: [PATCH 6/9] Remove unneeded initialization --- ext4/superblock.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext4/superblock.py b/ext4/superblock.py index abf5f42..b4e3bd6 100644 --- a/ext4/superblock.py +++ b/ext4/superblock.py @@ -173,7 +173,7 @@ def expected_checksum(self): @property def checksum(self): return ( - crc32c(bytes(self)[: Superblock.s_checksum.offset], 0xFFFFFFFF) + crc32c(bytes(self)[: Superblock.s_checksum.offset]) if self.metadata_csum else None ) @@ -183,7 +183,7 @@ def seed(self): if self.s_feature_incompat & EXT4_FEATURE_INCOMPAT.CSUM_SEED != 0: return self.s_checksum_seed - return crc32c(bytes(self.s_uuid), 0xFFFFFFFF) + return crc32c(bytes(self.s_uuid)) @property def desc_size(self): From f99117d1251f554fadee3bf8632c7a897c5a0b3a Mon Sep 17 00:00:00 2001 From: Nathaniel van Diepen Date: Thu, 11 Dec 2025 22:05:48 -0700 Subject: [PATCH 7/9] Add missing data --- ext4/inode.py | 14 +++++++------- test.py | 12 ++++++++++++ 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/ext4/inode.py b/ext4/inode.py index a332e2f..d75e027 100644 --- a/ext4/inode.py +++ b/ext4/inode.py @@ -249,15 +249,15 @@ def checksum(self): offset += Inode.i_checksum_hi.size csum = crc32c( - data[ - offset : self.EXT2_GOOD_OLD_INODE_SIZE - + min( - self.i_extra_isize, - self.superblock.s_inode_size - self.EXT2_GOOD_OLD_INODE_SIZE, - ) - ], + data[offset:], csum, ) + size = self.superblock.s_inode_size - len(data) + if size > 0: + self.volume.seek( + self.offset + self.EXT2_GOOD_OLD_INODE_SIZE + self.i_extra_isize + ) + csum = crc32c(self.volume.read(size), csum) if not self.has_hi: csum &= 0xFFFF diff --git a/test.py b/test.py index 4377f23..b68cf6b 100644 --- a/test.py +++ b/test.py @@ -65,6 +65,18 @@ def _assert(source: str): # Extract specific file volume = ext4.Volume(f, offset=offset) + + try: + print("Validate root inode: ", end="") + volume.root.validate() + print("pass") + + except ext4.struct.ChecksumError as e: + FAILED = True + print("fail") + print(" ", end="") + print(e) + inode = cast(ext4.File, volume.inode_at("/test.txt")) _assert("isinstance(inode, ext4.File)") b = inode.open() From 9ec25858d7ebc1593dceb523f66a1994cd888beb Mon Sep 17 00:00:00 2001 From: Nathaniel van Diepen Date: Thu, 11 Dec 2025 22:10:01 -0700 Subject: [PATCH 8/9] Move to helper --- ext4/inode.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/ext4/inode.py b/ext4/inode.py index d75e027..911e033 100644 --- a/ext4/inode.py +++ b/ext4/inode.py @@ -189,6 +189,16 @@ def __init__(self, volume, offset, i_no): super().__init__(volume, offset) self.tree = ExtentTree(self) + @property + def extra_inode_data(self) -> bytes: + if not self.has_hi: + return b"" + + size = sizeof(self) + assert size == self.EXT2_GOOD_OLD_INODE_SIZE + self.i_extra_isize + _ = self.volume.seek(self.offset + size) + return self.volume.read(self.superblock.s_inode_size - size) + @property def superblock(self): return self.volume.superblock @@ -252,12 +262,8 @@ def checksum(self): data[offset:], csum, ) - size = self.superblock.s_inode_size - len(data) - if size > 0: - self.volume.seek( - self.offset + self.EXT2_GOOD_OLD_INODE_SIZE + self.i_extra_isize - ) - csum = crc32c(self.volume.read(size), csum) + if self.superblock.s_inode_size - len(data) > 0: + csum = crc32c(self.extra_inode_data, csum) if not self.has_hi: csum &= 0xFFFF From 74fecb5657eb756095d3e3f6cdb15316ad90ba75 Mon Sep 17 00:00:00 2001 From: Nathaniel van Diepen Date: Thu, 11 Dec 2025 22:11:53 -0700 Subject: [PATCH 9/9] Normalize imports --- ext4/inode.py | 2 +- ext4/xattr.py | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/ext4/inode.py b/ext4/inode.py index 911e033..5ddad97 100644 --- a/ext4/inode.py +++ b/ext4/inode.py @@ -11,9 +11,9 @@ from ._compat import override +from .struct import crc32c from .struct import Ext4Struct from .struct import MagicError -from .struct import crc32c from .enum import EXT4_OS from .enum import EXT4_FL diff --git a/ext4/xattr.py b/ext4/xattr.py index 8c9afef..c092d9b 100644 --- a/ext4/xattr.py +++ b/ext4/xattr.py @@ -5,11 +5,13 @@ from ctypes import c_uint8 from ctypes import sizeof -from .struct import Ext4Struct -from .enum import EXT4_FL +from ._compat import override + from .enum import EXT4_FEATURE_INCOMPAT +from .enum import EXT4_FL + from .struct import crc32c -from ._compat import override +from .struct import Ext4Struct class ExtendedAttributeError(Exception):