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
21 changes: 17 additions & 4 deletions ubireader/debug.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,23 +17,36 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#############################################################

from __future__ import annotations
import sys
import traceback
from typing import Literal, NoReturn, Protocol, overload
from ubireader import settings

def log(obj, message):
class _Obj(Protocol):
__name__: str

def log(obj: _Obj, message: str) -> None:
if settings.logging_on or settings.logging_on_verbose:
print('{} {}'.format(obj.__name__, message))

def verbose_log(obj, message):
def verbose_log(obj: _Obj, message: str) -> None:
if settings.logging_on_verbose:
log(obj, message)

def verbose_display(displayable_obj):
class _Displayable(Protocol):
def display(self, tab: str) -> str: ...

def verbose_display(displayable_obj: _Displayable) -> None:
if settings.logging_on_verbose:
print(displayable_obj.display('\t'))

def error(obj, level, message):
@overload
def error(obj: _Obj, level: Literal['fatal', 'Fatal'], message: str) -> NoReturn: ...
@overload
def error(obj: _Obj, level: str, message: str) -> None: ...

def error(obj: _Obj, level: str, message: str) -> None:
if settings.error_action == 'exit':
print('{} {}: {}'.format(obj.__name__, level, message))
if settings.fatal_traceback:
Expand Down
Empty file added ubireader/py.typed
Empty file.
41 changes: 24 additions & 17 deletions ubireader/ubi/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,19 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#############################################################

from __future__ import annotations
from typing import TYPE_CHECKING
from ubireader.debug import error
from ubireader.ubi.block import sort, extract_blocks
from ubireader.ubi import display
from ubireader.ubi.image import description as image
from ubireader.ubi.block import layout, rm_old_blocks

if TYPE_CHECKING:
from ubireader.ubi_io import ubi_file as UbiFile
from ubireader.ubi.block import description as Block
from ubireader.ubi.image import description as Image

class ubi_base(object):
"""UBI Base object

Expand All @@ -39,7 +46,7 @@ class ubi_base(object):
Dict:blocks -- Dict keyed by PEB number of all blocks.
"""

def __init__(self, ubi_file):
def __init__(self, ubi_file: UbiFile) -> None:
self.__name__ = 'UBI'
self._file = ubi_file
self._first_peb_num = 0
Expand All @@ -54,7 +61,7 @@ def __init__(self, ubi_file):
self._leb_size = self.file.block_size - arbitrary_block.ec_hdr.data_offset


def _get_file(self):
def _get_file(self) -> UbiFile:
"""UBI File object

Returns:
Expand All @@ -64,7 +71,7 @@ def _get_file(self):
file = property(_get_file)


def _get_block_count(self):
def _get_block_count(self) -> int:
"""Total amount of UBI blocks in file.

Returns:
Expand All @@ -74,9 +81,9 @@ def _get_block_count(self):
block_count = property(_get_block_count)


def _set_first_peb_num(self, i):
def _set_first_peb_num(self, i: int) -> None:
self._first_peb_num = i
def _get_first_peb_num(self):
def _get_first_peb_num(self) -> int:
"""First Physical Erase Block with UBI data

Returns:
Expand All @@ -86,7 +93,7 @@ def _get_first_peb_num(self):
first_peb_num = property(_get_first_peb_num, _set_first_peb_num)


def _get_leb_size(self):
def _get_leb_size(self) -> int:
"""LEB size of UBI blocks in file.

Returns:
Expand All @@ -96,7 +103,7 @@ def _get_leb_size(self):
leb_size = property(_get_leb_size)


def _get_peb_size(self):
def _get_peb_size(self) -> int:
"""PEB size of UBI blocks in file.

Returns:
Expand All @@ -106,7 +113,7 @@ def _get_peb_size(self):
peb_size = property(_get_peb_size)


def _get_min_io_size(self):
def _get_min_io_size(self) -> int:
"""Min I/O Size

Returns:
Expand All @@ -116,7 +123,7 @@ def _get_min_io_size(self):
min_io_size = property(_get_min_io_size)


def _get_blocks(self):
def _get_blocks(self) -> dict[int, Block]:
"""Main Dict of UBI Blocks

Passed around for lists of indexes to be made or to be returned
Expand All @@ -142,7 +149,7 @@ class ubi(ubi_base):
List:unknown_blocks_list -- List of blocks with unknown types. *
"""

def __init__(self, ubi_file):
def __init__(self, ubi_file: UbiFile) -> None:
super(ubi, self).__init__(ubi_file)

layout_list, data_list, int_vol_list, unknown_list = sort.by_type(self.blocks)
Expand All @@ -161,12 +168,12 @@ def __init__(self, ubi_file):

layout_infos = layout.associate_blocks(self.blocks, layout_pairs)

self._images = []
self._images: list[Image] = []
for i in range(0, len(layout_infos)):
self._images.append(image(self.blocks, layout_infos[i]))


def _get_images(self):
def _get_images(self) -> list[Image]:
"""Get UBI images.

Returns:
Expand All @@ -176,7 +183,7 @@ def _get_images(self):
images = property(_get_images)


def _get_data_blocks_list(self):
def _get_data_blocks_list(self) -> list[int]:
"""Get all UBI blocks found in file that are data blocks.

Returns:
Expand All @@ -186,7 +193,7 @@ def _get_data_blocks_list(self):
data_blocks_list = property(_get_data_blocks_list)


def _get_layout_blocks_list(self):
def _get_layout_blocks_list(self) -> list[int]:
"""Get all UBI blocks found in file that are layout volume blocks.

Returns:
Expand All @@ -196,7 +203,7 @@ def _get_layout_blocks_list(self):
layout_blocks_list = property(_get_layout_blocks_list)


def _get_int_vol_blocks_list(self):
def _get_int_vol_blocks_list(self) -> list[int]:
"""Get all UBI blocks found in file that are internal volume blocks.

Returns:
Expand All @@ -208,7 +215,7 @@ def _get_int_vol_blocks_list(self):
int_vol_blocks_list = property(_get_int_vol_blocks_list)


def _get_unknown_blocks_list(self):
def _get_unknown_blocks_list(self) -> list[int]:
"""Get all UBI blocks found in file of unknown type..

Returns:
Expand All @@ -217,7 +224,7 @@ def _get_unknown_blocks_list(self):
return self._unknown_blocks_list
unknown_blocks_list = property(_get_unknown_blocks_list)

def display(self, tab=''):
def display(self, tab: str = '') -> str:
"""Print information about this object.

Argument:
Expand Down
30 changes: 19 additions & 11 deletions ubireader/ubi/block/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,19 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#############################################################

from __future__ import annotations
from typing import TYPE_CHECKING
from zlib import crc32
from ubireader import settings
from ubireader.debug import error, log, verbose_display, verbose_log
from ubireader.ubi import display
from ubireader.ubi.defines import UBI_EC_HDR_SZ, UBI_VID_HDR_SZ, UBI_INTERNAL_VOL_START, UBI_EC_HDR_MAGIC, UBI_CRC32_INIT
from ubireader.ubi.headers import ec_hdr, vid_hdr, vtbl_recs

if TYPE_CHECKING:
from collections.abc import Iterable, Mapping
from ubireader.ubi import ubi_base as UbiBase
from ubireader.ubi.headers import _vtbl_rec as VtblRec

class description(object):
"""UBI Block description Object
Expand All @@ -48,16 +54,18 @@ class description(object):
Will print out all information when invoked as a string.
"""

def __init__(self, block_buf):
data_crc: int

def __init__(self, block_buf: bytes) -> None:

self.file_offset = -1
self.peb_num = -1
self.leb_num = -1
self.size = -1

self.vid_hdr = None
self.vid_hdr: vid_hdr | None = None
self.is_internal_vol = False
self.vtbl_recs = []
self.vtbl_recs: list[VtblRec] = []

# TODO better understanding of block types/errors
self.ec_hdr = ec_hdr(block_buf[0:UBI_EC_HDR_SZ])
Expand All @@ -77,16 +85,16 @@ def __init__(self, block_buf):
self.is_valid = not self.ec_hdr.errors and not self.vid_hdr.errors or settings.ignore_block_header_errors


def __repr__(self):
def __repr__(self) -> str:
return 'Block: PEB# %s: LEB# %s' % (self.peb_num, self.leb_num)


def display(self, tab=''):
def display(self, tab: str ='') -> str:
return display.block(self, tab)



def get_blocks_in_list(blocks, idx_list):
def get_blocks_in_list(blocks: Mapping[int, description], idx_list: Iterable[int]) -> dict[int, description]:
"""Retrieve block objects in list of indexes

Arguments:
Expand All @@ -103,7 +111,7 @@ def get_blocks_in_list(blocks, idx_list):



def extract_blocks(ubi):
def extract_blocks(ubi: UbiBase) -> dict[int, description]:
"""Get a list of UBI block objects from file

Arguments:.
Expand All @@ -113,11 +121,11 @@ def extract_blocks(ubi):
Dict -- Of block objects keyed by PEB number.
"""

blocks = {}
blocks: dict[int, description] = {}
ubi.file.seek(ubi.file.start_offset)
peb_count = 0
cur_offset = 0
bad_blocks = []
bad_blocks: list[int] = []

# range instead of xrange, as xrange breaks > 4GB end_offset.
for i in range(ubi.file.start_offset, ubi.file.end_offset, ubi.file.block_size):
Expand Down Expand Up @@ -157,8 +165,8 @@ def extract_blocks(ubi):
return blocks


def rm_old_blocks(blocks, block_list):
del_blocks = []
def rm_old_blocks(blocks: Mapping[int, description], block_list: Iterable[int]) -> list[int]:
del_blocks: list[int] = []

for i in block_list:
if i in del_blocks:
Expand Down
23 changes: 19 additions & 4 deletions ubireader/ubi/block/layout.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,20 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#############################################################

from __future__ import annotations
from typing import TYPE_CHECKING, Literal, Protocol, overload
from ubireader.debug import log
from ubireader.ubi.block import sort

def group_pairs(blocks, layout_blocks_list):
if TYPE_CHECKING:
from collections.abc import Iterable, Mapping
from ubireader.ubi.block import description as Block

class _LayoutPair(Protocol):
def __getitem__(self, idx: Literal[0, 1], /) -> int: ...
def __contains__(self, key: object, /) -> bool: ...

def group_pairs(blocks: Mapping[int, Block], layout_blocks_list: Iterable[int]) -> list[_LayoutPair]:
"""Sort a list of layout blocks into pairs

Arguments:
Expand All @@ -31,7 +41,7 @@ def group_pairs(blocks, layout_blocks_list):
List -- Layout block pair indexes grouped in a list
"""

image_dict={}
image_dict: dict[int, _LayoutPair] = {}
for block_id in layout_blocks_list:
image_seq=blocks[block_id].ec_hdr.image_seq
if image_seq not in image_dict:
Expand All @@ -43,8 +53,13 @@ def group_pairs(blocks, layout_blocks_list):

return list(image_dict.values())

class _LayoutInfo(_LayoutPair, Protocol):
@overload
def __getitem__(self, idx: Literal[0, 1], /) -> int: ...
@overload
def __getitem__(self, idx: Literal[2], /) -> list[int]: ...

def associate_blocks(blocks, layout_pairs):
def associate_blocks(blocks: Mapping[int, Block], layout_pairs: list[_LayoutPair]) -> list[_LayoutInfo]:
"""Group block indexes with appropriate layout pairs

Arguments:
Expand All @@ -55,7 +70,7 @@ def associate_blocks(blocks, layout_pairs):
List -- Layout block pairs grouped with associated block ranges.
"""

seq_blocks = []
seq_blocks: list[int] = []
for layout_pair in layout_pairs:
seq_blocks = sort.by_image_seq(blocks, blocks[layout_pair[0]].ec_hdr.image_seq)
seq_blocks = [b for b in seq_blocks if b not in layout_pair]
Expand Down
Loading