|
| 1 | +import io |
| 2 | +from typing import Optional |
| 3 | + |
| 4 | +from unblob.extractors import Command |
| 5 | +from unblob.file_utils import ( |
| 6 | + Endian, |
| 7 | + InvalidInputFormat, |
| 8 | +) |
| 9 | +from unblob.models import ( |
| 10 | + File, |
| 11 | + HexString, |
| 12 | + StructHandler, |
| 13 | + ValidChunk, |
| 14 | +) |
| 15 | + |
| 16 | +C_DEFINITIONS = r""" |
| 17 | + typedef struct erofs_handler{ |
| 18 | + uint32_t magic; |
| 19 | + uint32_t crc32c; |
| 20 | + uint32_t feature_compact; |
| 21 | + uint8_t block_size_bs; |
| 22 | + uint8_t sb_extslots; |
| 23 | + uint16_t root_nid; |
| 24 | + uint64_t inos; |
| 25 | + uint64_t build_time; |
| 26 | + uint32_t build_time_nsec; |
| 27 | + uint32_t block_count; |
| 28 | + uint32_t meta_blkaddr; |
| 29 | + uint32_t xattr_blkaddr; |
| 30 | + uint8_t uuid[16]; |
| 31 | + char volume_name[16]; |
| 32 | + uint32_t feature_incompact; |
| 33 | + char reserved[44]; |
| 34 | + } erofs_handler_t; |
| 35 | +""" |
| 36 | + |
| 37 | +SUPERBLOCK_OFFSET = 0x400 |
| 38 | + |
| 39 | + |
| 40 | +class EROFSHandler(StructHandler): |
| 41 | + NAME = "erofs" |
| 42 | + PATTERNS = [HexString("e2 e1 f5 e0")] # Magic in little endian |
| 43 | + HEADER_STRUCT = "erofs_handler_t" |
| 44 | + C_DEFINITIONS = C_DEFINITIONS |
| 45 | + EXTRACTOR = Command( |
| 46 | + "fsck.erofs", |
| 47 | + "--no-preserve", |
| 48 | + "--extract={outdir}", |
| 49 | + "{inpath}", |
| 50 | + ) |
| 51 | + PATTERN_MATCH_OFFSET = -SUPERBLOCK_OFFSET |
| 52 | + |
| 53 | + def is_valid_header(self, header) -> bool: |
| 54 | + return ( |
| 55 | + header.block_count >= 1 |
| 56 | + and header.build_time > 0 |
| 57 | + and str(header.volume_name).isprintable() |
| 58 | + ) |
| 59 | + |
| 60 | + def calculate_chunk(self, file: File, start_offset: int) -> Optional[ValidChunk]: |
| 61 | + file.seek(SUPERBLOCK_OFFSET, io.SEEK_CUR) |
| 62 | + header = self.parse_header(file, Endian.LITTLE) |
| 63 | + if not self.is_valid_header(header): |
| 64 | + raise InvalidInputFormat("Invalid erofs header.") |
| 65 | + |
| 66 | + end_offset = (1 << header.block_size_bs) * header.block_count |
| 67 | + return ValidChunk( |
| 68 | + start_offset=start_offset, |
| 69 | + end_offset=end_offset, |
| 70 | + ) |
0 commit comments