Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/unblob/handlers/filesystem/cramfs.py: 100%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

35 statements  

1import binascii 

2import struct 

3from typing import Optional 

4 

5from unblob.extractors import Command 

6 

7from ...file_utils import Endian, convert_int32, get_endian 

8from ...models import ( 

9 File, 

10 HandlerDoc, 

11 HandlerType, 

12 HexString, 

13 Reference, 

14 StructHandler, 

15 ValidChunk, 

16) 

17 

18CRAMFS_FLAG_FSID_VERSION_2 = 0x00000001 

19BIG_ENDIAN_MAGIC = 0x28_CD_3D_45 

20 

21 

22def swap_int32(i): 

23 return struct.unpack("<I", struct.pack(">I", i))[0] 

24 

25 

26class CramFSHandler(StructHandler): 

27 NAME = "cramfs" 

28 

29 PATTERNS = [ 

30 HexString("28 CD 3D 45"), # big endian 

31 HexString("45 3D CD 28"), # little endian 

32 ] 

33 

34 C_DEFINITIONS = r""" 

35 typedef struct cramfs_header { 

36 uint32 magic; 

37 uint32 size; 

38 uint32 flags; 

39 uint32 future; 

40 char signature[16]; 

41 uint32 fsid_crc; 

42 uint32 fsid_edition; 

43 uint32 fsid_blocks; 

44 uint32 fsid_files; 

45 char name[16]; 

46 } cramfs_header_t; 

47 """ 

48 HEADER_STRUCT = "cramfs_header_t" 

49 

50 EXTRACTOR = Command("7z", "x", "-y", "{inpath}", "-o{outdir}") 

51 

52 DOC = HandlerDoc( 

53 name="CramFS", 

54 description="CramFS is a lightweight, read-only file system format designed for simplicity and efficiency in embedded systems. It uses zlib compression for file data and stores metadata in a compact, contiguous structure.", 

55 handler_type=HandlerType.FILESYSTEM, 

56 vendor=None, 

57 references=[ 

58 Reference( 

59 title="CramFS Documentation", 

60 url="https://web.archive.org/web/20160304053532/http://sourceforge.net/projects/cramfs/", 

61 ), 

62 Reference( 

63 title="CramFS Wikipedia", 

64 url="https://en.wikipedia.org/wiki/Cramfs", 

65 ), 

66 ], 

67 limitations=[], 

68 ) 

69 

70 def calculate_chunk(self, file: File, start_offset: int) -> Optional[ValidChunk]: 

71 endian = get_endian(file, BIG_ENDIAN_MAGIC) 

72 header = self.parse_header(file, endian) 

73 valid_signature = header.signature == b"Compressed ROMFS" 

74 

75 if valid_signature and self._is_crc_valid(file, start_offset, header, endian): 

76 return ValidChunk( 

77 start_offset=start_offset, 

78 end_offset=start_offset + header.size, 

79 ) 

80 return None 

81 

82 def _is_crc_valid( 

83 self, 

84 file: File, 

85 start_offset: int, 

86 header, 

87 endian: Endian, 

88 ) -> bool: 

89 # old cramfs format do not support crc 

90 if not (header.flags & CRAMFS_FLAG_FSID_VERSION_2): 

91 return True 

92 file.seek(start_offset) 

93 content = bytearray(file.read(header.size)) 

94 file.seek(start_offset + 32) 

95 crc_bytes = file.read(4) 

96 header_crc = convert_int32(crc_bytes, endian) 

97 content[32:36] = b"\x00\x00\x00\x00" 

98 computed_crc = binascii.crc32(content) 

99 # some vendors like their CRC's swapped, don't ask why 

100 return header_crc == computed_crc or header_crc == swap_int32(computed_crc)