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
16 changes: 16 additions & 0 deletions .github/workflows/build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ jobs:
run: pip install emake
- name: Run lint
run: emake lint
env:
PYTHONUNBUFFERED: "1"

test-image:
name: Generate test ext4 image
Expand All @@ -49,6 +51,8 @@ jobs:
run: |
set -e
./_test_image.sh
env:
PYTHONUNBUFFERED: "1"
- uses: actions/upload-artifact@v6
with:
name: test.ext4
Expand Down Expand Up @@ -88,6 +92,8 @@ jobs:
- name: Run tests
shell: bash
run: emake test
env:
PYTHONUNBUFFERED: "1"

fuzz:
name: Fuzz
Expand All @@ -112,6 +118,8 @@ jobs:
- name: Run test
shell: bash
run: make fuzz
env:
PYTHONUNBUFFERED: "1"

build-sdist:
name: Build sdist
Expand All @@ -125,6 +133,8 @@ jobs:
- *install-emake
- name: Building sdist
run: emake build --sdist
env:
PYTHONUNBUFFERED: "1"
- uses: actions/upload-artifact@v6
with:
name: pip-sdist
Expand All @@ -148,6 +158,8 @@ jobs:
path: .
- name: Test wheel
run: emake test --wheel
env:
PYTHONUNBUFFERED: "1"
- uses: actions/upload-artifact@v6
with:
name: pip-wheel-none-any
Expand Down Expand Up @@ -188,6 +200,8 @@ jobs:
--arch ${{ matrix.arch }} \
--libc ${{ matrix.libc }} \
--python ${{ matrix.python }}
env:
PYTHONUNBUFFERED: "1"
- name: Download test.ext4
uses: actions/download-artifact@v8
with:
Expand All @@ -200,6 +214,8 @@ jobs:
--arch ${{ matrix.arch }} \
--libc ${{ matrix.libc }} \
--python ${{ matrix.python }}
env:
PYTHONUNBUFFERED: "1"
- uses: actions/upload-artifact@v6
with:
name: pip-wheel-${{ matrix.python }}-${{ matrix.arch }}-${{ matrix.libc }}
Expand Down
18 changes: 17 additions & 1 deletion ext4/block.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import errno
import io
import os
from typing import TYPE_CHECKING
from typing import (
TYPE_CHECKING,
final,
)

from ._compat import override

Expand All @@ -11,7 +14,13 @@
from .volume import Volume


@final
class BlockIOBlocks:
__slots__ = (
"_null_block",
"blockio",
)

def __init__(self, blockio: "BlockIO") -> None:
self.blockio: BlockIO = blockio
self._null_block: bytearray = bytearray(self.block_size)
Expand Down Expand Up @@ -41,7 +50,14 @@ def __getitem__(self, ee_block: int) -> bytearray | bytes:
return self._null_block


@final
class BlockIO(io.RawIOBase):
__slots__ = (
"blocks",
"cursor",
"inode",
)

def __init__(self, inode: "Inode") -> None:
super().__init__()
self.inode: Inode = inode
Expand Down
2 changes: 2 additions & 0 deletions ext4/blockdescriptor.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@

@final
class BlockDescriptor(Ext4Struct):
__slots__ = ("bg_no",)

_pack_ = 1
# _anonymous_ = ("bg_reserved",)
_fields_ = [
Expand Down
7 changes: 7 additions & 0 deletions ext4/directory.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@


class DirectoryEntryStruct(Ext4Struct):
__slots__: tuple[str, ...] = ("directory",)

def __init__(self, directory: "Directory", offset: int) -> None:
self.directory: Directory = directory
super().__init__(directory.volume, offset)
Expand All @@ -39,6 +41,7 @@ def read_from_volume(self) -> None:


class DirectoryEntryBase(DirectoryEntryStruct):
__slots__: tuple[str, ...] = ()
@property
def name_bytes(self) -> bytes:
return bytes(self.name)[: self.name_len] # pyright: ignore[reportAny]
Expand All @@ -55,6 +58,7 @@ def is_fake_entry(self) -> bool:

@final
class DirectoryEntry(DirectoryEntryBase):
__slots__ = ()
_pack_ = 1
# _anonymous_ = ("l_i_reserved",)
_fields_ = [
Expand All @@ -67,6 +71,7 @@ class DirectoryEntry(DirectoryEntryBase):

@final
class DirectoryEntry2(DirectoryEntryBase):
__slots__ = ()
_pack_ = 1
# _anonymous_ = ("l_i_reserved",)
_fields_ = [
Expand All @@ -85,6 +90,7 @@ def is_fake_entry(self) -> bool:

@final
class DirectoryEntryTail(DirectoryEntryStruct):
__slots__ = ()
_pack_ = 1
# _anonymous_ = ("det_reserved_zero1", "det_reserved_zero2",)
_fields_ = [
Expand All @@ -107,6 +113,7 @@ def expected_magic(self) -> int:

@final
class DirectoryEntryHash(DirectoryEntryStruct):
__slots__ = ()
_pack_ = 1
# _anonymous_ = ("det_reserved_zero1", "det_reserved_zero2",)
_fields_ = [
Expand Down
31 changes: 31 additions & 0 deletions ext4/extent.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,13 @@
from .volume import Volume


@final
class ExtentBlocks:
__slots__: tuple[str, ...] = (
"_null_block",
"extent",
)

def __init__(self, extent: "Extent") -> None:
self.extent: Extent = extent
self._null_block: bytearray = bytearray(self.block_size)
Expand Down Expand Up @@ -74,6 +80,13 @@ def __len__(self) -> int:

@final
class ExtentHeader(Ext4Struct):
__slots__ = (
"extents",
"indices",
"tail",
"tree",
)

_pack_ = 1
# _anonymous_ = ()
_fields_ = [
Expand Down Expand Up @@ -153,6 +166,11 @@ def checksum(self) -> int | None:

@final
class ExtentIndex(Ext4Struct):
__slots__ = (
"ei_no",
"header",
)

_pack_ = 1
# _anonymous_ = ("ei_unused",)
_fields_ = [
Expand Down Expand Up @@ -184,6 +202,12 @@ def inode(self) -> "Inode":

@final
class Extent(Ext4Struct):
__slots__ = (
"blocks",
"ee_no",
"header",
)

_pack_ = 1
# _anonymous_ = ("ei_unused",)
_fields_ = [
Expand Down Expand Up @@ -231,6 +255,8 @@ def read(self) -> bytes:

@final
class ExtentTail(Ext4Struct):
__slots__ = ("header",)

_pack_ = 1
_fields_ = [
("et_checksum", c_uint32),
Expand All @@ -250,6 +276,11 @@ def inode(self) -> "Inode":


class ExtentTree:
__slots__: tuple[str, ...] = (
"headers",
"inode",
)

def __init__(self, inode: "Inode") -> None:
self.inode: Inode = inode
self.headers: list[ExtentHeader] = []
Expand Down
21 changes: 21 additions & 0 deletions ext4/htree.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@


class LittleEndianStructureWithVolume(LittleEndianStructure):
__slots__: tuple[str, ...] = ("_volume",)

def __init__(self) -> None:
super().__init__()
self._volume: Volume | None = None
Expand All @@ -47,6 +49,8 @@ def volume(self, volume: "Volume") -> None:

@final
class DotDirectoryEntry2(LittleEndianStructureWithVolume):
__slots__ = ()

_pack_ = 1
# _anonymous_ = ()
_fields_ = [
Expand Down Expand Up @@ -76,6 +80,7 @@ def verify(self) -> None:

@final
class DXRootInfo(LittleEndianStructure):
__slots__ = ()
_pack_ = 1
# _anonymous_ = ("reserved_zero")
_fields_ = [
Expand All @@ -88,6 +93,8 @@ class DXRootInfo(LittleEndianStructure):


class DXBase(Ext4Struct):
__slots__: tuple[str, ...] = ("directory",)

def __init__(self, directory: "Directory", offset: int) -> None:
self.directory: Directory = directory
super().__init__(directory.volume, offset)
Expand All @@ -102,6 +109,11 @@ def read_from_volume(self) -> None:

@final
class DXEntry(DXBase):
__slots__ = (
"index",
"parent",
)

_pack_ = 1
# _anonymous_ = ("")
_fields_ = [
Expand All @@ -119,6 +131,8 @@ def __init__(self, parent: "DXEntriesBase", index: int) -> None:


class DXEntriesBase(DXBase):
__slots__: tuple[str, ...] = ()

@override
def read_from_volume(self) -> None:
super().read_from_volume()
Expand All @@ -141,6 +155,8 @@ def info_length(self) -> int:

@final
class DXRoot(DXEntriesBase):
__slots__ = ()

_pack_ = 1
# _anonymous_ = ("")
_fields_ = [
Expand All @@ -159,6 +175,7 @@ def __init__(self, inode: "Directory") -> None:

@final
class DXFake(LittleEndianStructure):
__slots__ = ()
_pack_ = 1
# _anonymous_ = ("")
_fields_ = [
Expand All @@ -178,6 +195,8 @@ def magic(self) -> int:

@final
class DXNode(DXEntriesBase):
__slots__ = ()

_pack_ = 1
# _anonymous_ = ("")
_fields_ = [
Expand All @@ -196,6 +215,8 @@ def __init__(self, directory: "Directory", offset: int) -> None:

@final
class DXTail(DXBase):
__slots__ = ("parent",)

_pack_ = 1
# _anonymous_ = ("dt_reserved")
_fields_ = [
Expand Down
Loading
Loading