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

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

87 statements  

1import binascii 

2import io 

3from typing import Optional 

4 

5from structlog import get_logger 

6 

7from unblob.file_utils import ( 

8 Endian, 

9 InvalidInputFormat, 

10 convert_int16, 

11 read_until_past, 

12 round_up, 

13) 

14 

15from ...extractors import Command 

16from ...models import ( 

17 File, 

18 HandlerDoc, 

19 HandlerType, 

20 HexString, 

21 Reference, 

22 StructHandler, 

23 ValidChunk, 

24) 

25 

26logger = get_logger() 

27 

28 

29BLOCK_ALIGNMENT = 4 

30JFFS2_MAGICS = [0x1985, 0x8519, 0x1984, 0x8419] 

31 

32# Compatibility flags. 

33JFFS2_NODE_ACCURATE = 0x2000 

34JFFS2_FEATURE_INCOMPAT = 0xC000 

35JFFS2_FEATURE_RWCOMPAT_DELETE = 0x0000 

36 

37DIRENT = JFFS2_FEATURE_INCOMPAT | JFFS2_NODE_ACCURATE | 1 

38INODE = JFFS2_FEATURE_INCOMPAT | JFFS2_NODE_ACCURATE | 2 

39CLEANMARKER = JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 3 

40PADDING = JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 4 

41SUMMARY = JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 6 

42XATTR = JFFS2_FEATURE_INCOMPAT | JFFS2_NODE_ACCURATE | 8 

43XREF = JFFS2_FEATURE_INCOMPAT | JFFS2_NODE_ACCURATE | 9 

44 

45JFFS2_NODETYPES = {DIRENT, INODE, CLEANMARKER, PADDING, SUMMARY, XATTR, XREF} 

46 

47 

48class _JFFS2Base(StructHandler): 

49 C_DEFINITIONS = r""" 

50 typedef struct jffs2_unknown_node 

51 { 

52 uint16 magic; 

53 uint16 nodetype; 

54 uint32 totlen; 

55 uint32 hdr_crc; 

56 } jffs2_unknown_node_t; 

57 """ 

58 

59 HEADER_STRUCT = "jffs2_unknown_node_t" 

60 

61 BIG_ENDIAN_MAGIC = 0x19_85 

62 

63 EXTRACTOR = Command("jefferson", "-v", "-f", "-d", "{outdir}", "{inpath}") 

64 

65 def guess_endian(self, file: File) -> Endian: 

66 magic = convert_int16(file.read(2), Endian.BIG) 

67 endian = Endian.BIG if magic == self.BIG_ENDIAN_MAGIC else Endian.LITTLE 

68 file.seek(-2, io.SEEK_CUR) 

69 return endian 

70 

71 def valid_header(self, header, node_start_offset: int, eof: int) -> bool: 

72 header_crc = (binascii.crc32(header.dumps()[:-4], -1) ^ -1) & 0xFFFFFFFF 

73 check_crc = True 

74 

75 if header.nodetype not in JFFS2_NODETYPES: 

76 if header.nodetype | JFFS2_NODE_ACCURATE not in JFFS2_NODETYPES: 

77 logger.debug( 

78 "Invalid JFFS2 node type", node_type=header.nodetype, _verbosity=2 

79 ) 

80 return False 

81 logger.debug( 

82 "Not accurate JFFS2 node type, ignore CRC", 

83 node_type=header.nodetype, 

84 _verbosity=2, 

85 ) 

86 check_crc = False 

87 

88 if check_crc and header_crc != header.hdr_crc: 

89 logger.debug("node header CRC missmatch", _verbosity=2) 

90 return False 

91 

92 if node_start_offset + header.totlen > eof: 

93 logger.debug( 

94 "node length greater than total file size", 

95 node_len=header.totlen, 

96 file_size=eof, 

97 _verbosity=2, 

98 ) 

99 return False 

100 

101 if header.totlen < len(header): 

102 logger.debug( 

103 "node length greater than header size", 

104 node_len=header.totlen, 

105 _verbosity=2, 

106 ) 

107 return False 

108 return True 

109 

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

111 file.seek(0, io.SEEK_END) 

112 eof = file.tell() 

113 file.seek(start_offset) 

114 

115 endian = self.guess_endian(file) 

116 current_offset = start_offset 

117 

118 while current_offset < eof: 

119 node_start_offset = current_offset 

120 file.seek(current_offset) 

121 try: 

122 header = self.parse_header(file, endian=endian) 

123 except EOFError: 

124 break 

125 

126 if header.magic not in JFFS2_MAGICS: 

127 # JFFS2 allows padding at the end with 0xFF or 0x00, usually 

128 # to the size of an erase block. 

129 if header.magic in [0x0000, 0xFFFF]: 

130 file.seek(-len(header), io.SEEK_CUR) 

131 current_offset = read_until_past(file, b"\x00\xff") 

132 continue 

133 

134 logger.debug( 

135 "unexpected header magic", 

136 header_magic=header.magic, 

137 _verbosity=2, 

138 ) 

139 break 

140 

141 if not self.valid_header(header, node_start_offset, eof): 

142 return None 

143 

144 node_len = round_up(header.totlen, BLOCK_ALIGNMENT) 

145 current_offset += node_len 

146 

147 if current_offset > eof: 

148 raise InvalidInputFormat("Corrupt file or last chunk isn't really JFFS2") 

149 

150 return ValidChunk( 

151 start_offset=start_offset, 

152 end_offset=current_offset, 

153 ) 

154 

155 

156class JFFS2OldHandler(_JFFS2Base): 

157 NAME = "jffs2_old" 

158 

159 PATTERNS = [ 

160 HexString("84 19 ( 01 | 02 | 03 | 04 | 06 | 08 | 09 ) ( e0 | 20 )"), # LE 

161 HexString("19 84 ( e0 | 20 ) ( 01 | 02 | 03 | 04 | 06 | 08 | 09 )"), # BE 

162 ] 

163 

164 BIG_ENDIAN_MAGIC = 0x19_84 

165 

166 DOC = HandlerDoc( 

167 name="JFFS2 (old)", 

168 description="JFFS2 (Journaling Flash File System version 2) is a log-structured file system for flash memory devices, using an older magic number to identify its nodes. It organizes data into nodes with headers containing metadata and CRC checks for integrity.", 

169 handler_type=HandlerType.FILESYSTEM, 

170 vendor=None, 

171 references=[ 

172 Reference( 

173 title="JFFS2 Documentation", 

174 url="https://sourceware.org/jffs2/", 

175 ), 

176 Reference( 

177 title="JFFS2 Wikipedia", 

178 url="https://en.wikipedia.org/wiki/JFFS2", 

179 ), 

180 ], 

181 limitations=[], 

182 ) 

183 

184 

185class JFFS2NewHandler(_JFFS2Base): 

186 NAME = "jffs2_new" 

187 

188 PATTERNS = [ 

189 HexString("85 19 ( 01 | 02 | 03 | 04 | 06 | 08 | 09 ) ( e0 | 20 )"), # LE 

190 HexString("19 85 ( e0 | 20 ) ( 01 | 02 | 03 | 04 | 06 | 08 | 09 )"), # BE 

191 ] 

192 

193 BIG_ENDIAN_MAGIC = 0x19_85 

194 

195 DOC = HandlerDoc( 

196 name="JFFS2 (new)", 

197 description="JFFS2 (Journaling Flash File System version 2) is a log-structured file system for flash memory devices, using an older magic number to identify its nodes. It organizes data into nodes with headers containing metadata and CRC checks for integrity.", 

198 handler_type=HandlerType.FILESYSTEM, 

199 vendor=None, 

200 references=[ 

201 Reference( 

202 title="JFFS2 Documentation", 

203 url="https://sourceware.org/jffs2/", 

204 ), 

205 Reference( 

206 title="JFFS2 Wikipedia", 

207 url="https://en.wikipedia.org/wiki/JFFS2", 

208 ), 

209 ], 

210 limitations=[], 

211 )