Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 21 additions & 10 deletions .github/workflows/build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ on:
pull_request:
workflow_dispatch:
release:
types: [released]
types: [ released ]
permissions: read-all
jobs:
lint:
Expand All @@ -16,7 +16,6 @@ jobs:
fail-fast: false
matrix:
python: &python-versions
- "3.10"
- "3.11"
- "3.12"
- "3.13"
Expand Down Expand Up @@ -61,7 +60,7 @@ jobs:
test:
name: Test on ${{ matrix.os }} python ${{ matrix.python }}
runs-on: ${{ matrix.os }}
needs: [test-image]
needs: [ test-image ]
strategy:
fail-fast: false
matrix:
Expand Down Expand Up @@ -93,7 +92,6 @@ jobs:
fail-fast: false
matrix:
python:
#- "3.10" # atheris appears to break
- "3.11"
- "3.12"
- "3.13"
Expand All @@ -112,7 +110,7 @@ jobs:
build-sdist:
name: Build pip package
runs-on: ubuntu-latest
needs: [test, lint]
needs: [ test, lint ]
steps:
- name: Checkout the Git repository
uses: actions/checkout@v6
Expand All @@ -133,7 +131,7 @@ jobs:
build-any-wheel:
name: Build wheel
runs-on: ubuntu-latest
needs: [test, lint]
needs: [ test, lint ]
steps:
- name: Checkout the Git repository
uses: actions/checkout@v6
Expand All @@ -153,8 +151,8 @@ jobs:
if-no-files-found: error
build-wheel:
name: Build wheel
if: github.repository == 'Eeems/python-ext4' && github.event_name == 'release' && startsWith(github.ref, 'refs/tags')
needs: [lint, test]
# if: github.repository == 'Eeems/python-ext4' && github.event_name == 'release' && startsWith(github.ref, 'refs/tags')
needs: [ lint, test, test-image ]
runs-on: ubuntu-latest
strategy:
fail-fast: false
Expand All @@ -166,18 +164,31 @@ jobs:
- ppc64le
- aarch64
- armv7l
- riscv64
- s390x
libc:
- glibc
# - musl
- musl
steps:
- name: Checkout the Git repository
uses: actions/checkout@v6
- name: Building package
- name: Building wheel
run: ./wheel.sh
env:
python: ${{ matrix.python }}
arch: ${{ matrix.arch }}
libc: ${{ matrix.libc }}
- name: Download test.ext4
uses: actions/download-artifact@v8
with:
name: test.ext4
path: .
- name: Testing wheel
run: ./test-wheel.sh
env:
python: ${{ matrix.python }}
arch: ${{ matrix.arch }}
libc: ${{ matrix.libc }}
- uses: actions/upload-artifact@v6
with:
name: pip-wheel-${{ matrix.python }}-${{ matrix.arch }}-${{ matrix.libc }}
Expand Down
2 changes: 1 addition & 1 deletion ext4/blockdescriptor.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class BlockDescriptor(Ext4Struct):
("bg_free_blocks_count_lo", c_uint16),
("bg_free_inodes_count_lo", c_uint16),
("bg_used_dirs_count_lo", c_uint16),
("bg_flags", EXT4_BG),
("bg_flags", EXT4_BG.basetype),
("bg_exclude_bitmap_lo", c_uint32),
("bg_block_bitmap_csum_lo", c_uint16),
("bg_inode_bitmap_csum_lo", c_uint16),
Expand Down
4 changes: 2 additions & 2 deletions ext4/directory.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,13 +73,13 @@ class DirectoryEntry2(DirectoryEntryBase):
("inode", c_uint32),
("rec_len", c_uint16),
("name_len", c_uint8),
("file_type", EXT4_FT),
("file_type", EXT4_FT.basetype),
("name", c_char * EXT4_NAME_LEN),
]

@DirectoryEntryBase.is_fake_entry.getter
def is_fake_entry(self) -> bool:
file_type = assert_cast(self.file_type, EXT4_FT) # pyright: ignore[reportAny]
file_type = EXT4_FT(self.file_type) # pyright: ignore[reportAny]
return super().is_fake_entry or file_type == EXT4_FT.DIR_CSUM


Expand Down
1 change: 1 addition & 0 deletions ext4/enum.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ def __repr__(self) -> str:
def TypedCEnumeration(_type: type["SimpleCData"]): # noqa: ANN201
class CEnumeration(_type, metaclass=TypedEnumerationType(_type)): # pyright: ignore[reportGeneralTypeIssues, reportUntypedBaseClass] # noqa: ANN201,PLW1641,PLW1641
_members_: dict[str, Any] = {} # pyright: ignore[reportExplicitAny]
basetype: type["SimpleCData"] = _type

@override
def __repr__(self) -> str:
Expand Down
2 changes: 1 addition & 1 deletion ext4/htree.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ class DXRootInfo(LittleEndianStructure):
# _anonymous_ = ("reserved_zero")
_fields_ = [
("reserved_zero", c_uint32),
("hash_version", DX_HASH),
("hash_version", DX_HASH.basetype),
("info_length", c_uint8),
("indirect_levels", c_uint8),
("unused_flags", c_uint8),
Expand Down
27 changes: 11 additions & 16 deletions ext4/inode.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,14 @@
from collections.abc import Generator
from ctypes import (
LittleEndianStructure,
Union,
LittleEndianUnion,
c_uint16,
c_uint32,
sizeof,
)
from typing import (
TYPE_CHECKING,
Any,
cast,
final,
)

Expand Down Expand Up @@ -103,7 +102,7 @@ class Masix1(LittleEndianStructure):


@final
class Osd1(Union):
class Osd1(LittleEndianUnion):
_pack_ = 1
_fields_ = [
("linux1", Linux1),
Expand Down Expand Up @@ -151,7 +150,7 @@ class Masix2(LittleEndianStructure):


@final
class Osd2(Union):
class Osd2(LittleEndianUnion):
_pack_ = 1
_fields_ = [
("linux2", Linux2),
Expand All @@ -165,7 +164,7 @@ class Inode(Ext4Struct):
EXT2_GOOD_OLD_INODE_SIZE: int = 128
_pack_ = 1 # pyright: ignore[reportUnannotatedClassAttribute]
_fields_ = [ # pyright: ignore[reportUnannotatedClassAttribute]
("i_mode", MODE),
("i_mode", MODE.basetype),
("i_uid", c_uint16),
("i_size_lo", c_uint32),
("i_atime", c_uint32),
Expand All @@ -175,7 +174,7 @@ class Inode(Ext4Struct):
("i_gid", c_uint16),
("i_links_count", c_uint16),
("i_blocks_lo", c_uint32),
("i_flags", EXT4_FL),
("i_flags", EXT4_FL.basetype),
("osd1", Osd1),
("i_block", c_uint32 * 15),
("i_generation", c_uint32),
Expand All @@ -197,12 +196,8 @@ class Inode(Ext4Struct):
@classmethod
def get_file_type(cls, volume: Volume, offset: int) -> EXT4_FT:
_ = volume.seek(offset + Inode.i_mode.offset)
file_type = cast(
MODE,
Inode.field_type("i_mode").from_buffer_copy( # pyright: ignore[reportAttributeAccessIssue, reportUnknownMemberType, reportOptionalMemberAccess]
volume.read(Inode.i_mode.size)
)
& 0xF000,
file_type = MODE(
int.from_bytes(volume.read(Inode.i_mode.size), "little") & 0xF000
)
match file_type:
case MODE.IFIFO:
Expand Down Expand Up @@ -321,7 +316,7 @@ def seed(self) -> int:

@Ext4Struct.checksum.getter
def checksum(self) -> int | None:
s_creator_os: EXT4_OS = assert_cast(self.superblock.s_creator_os, EXT4_OS) # pyright: ignore[reportAny]
s_creator_os: EXT4_OS = EXT4_OS(self.superblock.s_creator_os) # pyright: ignore[reportAny]
if s_creator_os != EXT4_OS.LINUX:
return None

Expand Down Expand Up @@ -358,7 +353,7 @@ def checksum(self) -> int | None:

@Ext4Struct.expected_checksum.getter
def expected_checksum(self) -> int | None:
s_creator_os = assert_cast(self.superblock.s_creator_os, EXT4_OS) # pyright: ignore[reportAny]
s_creator_os = EXT4_OS(self.superblock.s_creator_os) # pyright: ignore[reportAny]
if s_creator_os != EXT4_OS.LINUX:
return None

Expand All @@ -378,7 +373,7 @@ def validate(self) -> None:
self.tree.validate()

def has_flag(self, flag: EXT4_FL | int) -> bool:
i_flags = assert_cast(self.i_flags, EXT4_FL) # pyright: ignore[reportAny]
i_flags = EXT4_FL(self.i_flags) # pyright: ignore[reportAny]
return (i_flags & flag) != 0

@property
Expand Down Expand Up @@ -610,7 +605,7 @@ def opendir(
) -> Generator[tuple[DirectoryEntry | DirectoryEntry2, EXT4_FT], Any, None]: # pyright: ignore[reportExplicitAny]
for dirent in self._opendir():
if isinstance(dirent, DirectoryEntry2):
file_type = assert_cast(dirent.file_type, EXT4_FT) # pyright: ignore[reportAny]
file_type = EXT4_FT(dirent.file_type) # pyright: ignore[reportAny]
if file_type == EXT4_FT.DIR_CSUM:
continue

Expand Down
40 changes: 17 additions & 23 deletions ext4/superblock.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ class Superblock(Ext4Struct):
# "s_reserved_pad",
# "s_reserved",
# )
_fields_ = [ # pyright: ignore[reportUnknownVariableType]
_fields_ = [
("s_inodes_count", c_uint32),
("s_blocks_count_lo", c_uint32),
("s_r_blocks_count_lo", c_uint32),
Expand All @@ -59,21 +59,21 @@ class Superblock(Ext4Struct):
("s_mnt_count", c_uint16),
("s_max_mnt_count", c_uint16),
("s_magic", c_uint16), # 0xEF53
("s_state", EXT4_FS),
("s_errors", EXT4_ERRORS),
("s_state", EXT4_FS.basetype),
("s_errors", EXT4_ERRORS.basetype),
("s_minor_rev_level", c_uint16),
("s_lastcheck", c_uint32),
("s_checkinterval", c_uint32),
("s_creator_os", EXT4_OS),
("s_rev_level", EXT4_REV),
("s_creator_os", EXT4_OS.basetype),
("s_rev_level", EXT4_REV.basetype),
("s_def_resuid", c_uint16),
("s_def_resgid", c_uint16),
("s_first_ino", c_uint32),
("s_inode_size", c_uint16),
("s_block_group_nr", c_uint16),
("s_feature_compat", EXT4_FEATURE_COMPAT),
("s_feature_incompat", EXT4_FEATURE_INCOMPAT),
("s_feature_ro_compat", EXT4_FEATURE_RO_COMPAT),
("s_feature_compat", EXT4_FEATURE_COMPAT.basetype),
("s_feature_incompat", EXT4_FEATURE_INCOMPAT.basetype),
("s_feature_ro_compat", EXT4_FEATURE_RO_COMPAT.basetype),
("s_uuid", c_uint8 * 16),
("s_volume_name", c_ubyte * 16),
("s_last_mounted", c_ubyte * 64),
Expand All @@ -86,10 +86,10 @@ class Superblock(Ext4Struct):
("s_journal_dev", c_uint32),
("s_last_orphan", c_uint32),
("s_hash_seed", c_uint32 * 4),
("s_def_hash_version", DX_HASH),
("s_def_hash_version", DX_HASH.basetype),
("s_jnl_backup_type", c_uint8),
("s_desc_size", c_uint16),
("s_default_mount_opts", EXT4_DEFM),
("s_default_mount_opts", EXT4_DEFM.basetype),
("s_first_meta_bg", c_uint32),
("s_mkfs_time", c_uint32),
("s_jnl_blocks", c_uint32 * 17),
Expand All @@ -98,13 +98,13 @@ class Superblock(Ext4Struct):
("s_free_blocks_count_hi", c_uint32),
("s_min_extra_isize", c_uint16),
("s_want_extra_isize", c_uint16),
("s_flags", EXT2_FLAGS),
("s_flags", EXT2_FLAGS.basetype),
("s_raid_stride", c_uint16),
("s_mmp_interval", c_uint16),
("s_mmp_block", c_uint64),
("s_raid_stripe_width", c_uint32),
("s_log_groups_per_flex", c_uint8),
("s_checksum_type", EXT4_CHKSUM),
("s_checksum_type", EXT4_CHKSUM.basetype),
("s_reserved_pad", c_uint16),
("s_kbytes_written", c_uint64),
("s_snapshot_inum", c_uint32),
Expand All @@ -122,12 +122,12 @@ class Superblock(Ext4Struct):
("s_last_error_line", c_uint32),
("s_last_error_block", c_uint64),
("s_last_error_func", c_uint8 * 32),
("s_mount_opts", EXT4_MOUNT * 64), # pyright: ignore[reportOperatorIssue]
("s_mount_opts", EXT4_MOUNT.basetype * 64),
("s_usr_quota_inum", c_uint32),
("s_grp_quota_inum", c_uint32),
("s_overhead_blocks", c_uint32),
("s_backup_bgs", c_uint32 * 2),
("s_encrypt_algos", FS_ENCRYPTION_MODE * 4), # pyright: ignore[reportOperatorIssue]
("s_encrypt_algos", FS_ENCRYPTION_MODE.basetype * 4),
("s_encrypt_pw_salt", c_uint8 * 16),
("s_lpf_ino", c_uint32),
("s_prj_quota_inum", c_uint32),
Expand Down Expand Up @@ -206,21 +206,15 @@ def checksum(self) -> int | None:

@property
def feature_incompat(self) -> EXT4_FEATURE_INCOMPAT:
s_feature_incompat = assert_cast(self.s_feature_incompat, EXT4_FEATURE_INCOMPAT) # pyright: ignore[reportAny]
return s_feature_incompat
return EXT4_FEATURE_INCOMPAT(self.s_feature_incompat) # pyright: ignore[reportAny]

@property
def feature_compat(self) -> EXT4_FEATURE_COMPAT:
s_feature_compat = assert_cast(self.s_feature_compat, EXT4_FEATURE_COMPAT) # pyright: ignore[reportAny]
return s_feature_compat
return EXT4_FEATURE_COMPAT(self.s_feature_compat) # pyright: ignore[reportAny]

@property
def feature_ro_compat(self) -> EXT4_FEATURE_RO_COMPAT:
s_feature_ro_compat = assert_cast(
self.s_feature_ro_compat, # pyright: ignore[reportAny]
EXT4_FEATURE_RO_COMPAT,
)
return s_feature_ro_compat
return EXT4_FEATURE_RO_COMPAT(self.s_feature_ro_compat) # pyright: ignore[reportAny]

@property
def seed(self) -> int:
Expand Down
9 changes: 3 additions & 6 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
[project]
name = "ext4"
version = "1.3.1"
version = "1.3.2"
authors = [
{ name="Eeems", email="eeems@eeems.email" },
]
description = "Library for read only interactions with an ext4 filesystem"
requires-python = ">=3.10"
requires-python = ">=3.11"
classifiers = [
"Development Status :: 4 - Beta",
"Environment :: Console",
Expand All @@ -15,7 +15,6 @@ classifiers = [
"Operating System :: Microsoft :: Windows",
"Programming Language :: Python :: 3 :: Only",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
Expand All @@ -37,9 +36,7 @@ dev = [
'ruff',
'basedpyright',
]
test = [
"pytest",
]
test = []
fuzz = [
"atheris",
]
Expand Down
Loading
Loading