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

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

101 statements  

1from pathlib import Path 

2 

3from structlog import get_logger 

4 

5from unblob.extractors import Command 

6 

7from ...file_utils import Endian, StructParser, get_endian, round_up 

8from ...models import ( 

9 Extractor, 

10 File, 

11 HandlerDoc, 

12 HandlerType, 

13 HexString, 

14 Reference, 

15 StructHandler, 

16 ValidChunk, 

17) 

18 

19logger = get_logger() 

20 

21PAD_SIZES = [4_096, 1_024] 

22 

23 

24class SquashFSExtractor(Extractor): 

25 EXECUTABLE = "sasquatch" 

26 

27 def __init__(self, big_endian_magic: int): 

28 self.big_endian_magic = big_endian_magic 

29 

30 def extract(self, inpath: Path, outdir: Path): 

31 with File.from_path(inpath) as file: 

32 endian = get_endian(file, self.big_endian_magic) 

33 

34 commands_args = [] 

35 

36 if endian == Endian.BIG: 

37 commands_args.append("-be") 

38 else: 

39 commands_args.append("-le") 

40 

41 commands_args.extend( 

42 [ 

43 "-no-exit-code", 

44 "-f", 

45 "-d", 

46 "{outdir}", 

47 "{inpath}", 

48 ] 

49 ) 

50 extractor = Command(self.EXECUTABLE, *commands_args) 

51 extractor.extract(inpath, outdir) 

52 

53 def get_dependencies(self) -> list[str]: 

54 return [self.EXECUTABLE] 

55 

56 

57class _SquashFSBase(StructHandler): 

58 BIG_ENDIAN_MAGIC = 0x73_71_73_68 

59 

60 EXTRACTOR = SquashFSExtractor(0x73_71_73_68) 

61 

62 def calculate_chunk(self, file: File, start_offset: int) -> ValidChunk | None: 

63 file.seek(start_offset) 

64 endian = get_endian(file, self.BIG_ENDIAN_MAGIC) 

65 header = self.parse_header(file, endian) 

66 

67 end_of_data_offset = (start_offset + header.bytes_used) & 0xFF_FF_FF_FF 

68 file.seek(end_of_data_offset) 

69 padding = file.read( 

70 round_up(header.bytes_used, max(PAD_SIZES)) - header.bytes_used 

71 ) 

72 

73 for pad_size in sorted(PAD_SIZES, reverse=True): 

74 size = round_up(header.bytes_used, pad_size) 

75 padding_length = size - header.bytes_used 

76 

77 if padding.startswith(b"\00" * padding_length): 

78 end_offset = start_offset + size 

79 return ValidChunk(start_offset=start_offset, end_offset=end_offset) 

80 

81 return ValidChunk( 

82 start_offset=start_offset, end_offset=start_offset + header.bytes_used 

83 ) 

84 

85 

86class SquashFSv1Handler(_SquashFSBase): 

87 NAME = "squashfs_v1" 

88 

89 PATTERNS = [ 

90 HexString( 

91 """ 

92 // 00000000 73 71 73 68 00 00 00 03 00 00 00 00 00 00 00 00 |sqsh............| 

93 // 00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 |................| 

94 // squashfs_v1_magic_be 

95 73 71 73 68 [24] 00 01 

96 """ 

97 ), 

98 HexString( 

99 """ 

100 // 00000000 68 73 71 73 03 00 00 00 00 00 00 00 00 00 00 00 |hsqs............| 

101 // 00000010 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 |................| 

102 // squashfs_v1_magic_le 

103 68 73 71 73 [24] 01 00 

104 """ 

105 ), 

106 ] 

107 

108 C_DEFINITIONS = r""" 

109 typedef struct squashfs_super_block 

110 { 

111 char s_magic[4]; 

112 uint32 inodes; 

113 uint32 bytes_used; 

114 uint32 uid_start; 

115 uint32 guid_start; 

116 uint32 inode_table_start; 

117 uint32 directory_table_start; 

118 uint16 s_major; 

119 uint16 s_minor; 

120 } squashfs_super_block_t; 

121 """ 

122 HEADER_STRUCT = "squashfs_super_block_t" 

123 

124 DOC = HandlerDoc( 

125 name="SquashFS (v1)", 

126 description="SquashFS version 1 is a compressed, read-only file system format designed for minimal storage usage. It is commonly used in embedded systems and early Linux distributions.", 

127 handler_type=HandlerType.FILESYSTEM, 

128 vendor=None, 

129 references=[ 

130 Reference( 

131 title="SquashFS Documentation", 

132 url="https://dr-emann.github.io/squashfs/", 

133 ), 

134 Reference( 

135 title="SquashFS Wikipedia", 

136 url="https://en.wikipedia.org/wiki/SquashFS", 

137 ), 

138 ], 

139 limitations=[], 

140 ) 

141 

142 

143class SquashFSv2Handler(SquashFSv1Handler): 

144 NAME = "squashfs_v2" 

145 

146 PATTERNS = [ 

147 HexString( 

148 """ 

149 // 00000000 73 71 73 68 00 00 00 03 00 00 00 00 00 00 00 00 |sqsh............| 

150 // 00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 02 00 00 |................| 

151 // squashfs_v2_magic_be 

152 73 71 73 68 [24] 00 02 

153 """ 

154 ), 

155 HexString( 

156 """ 

157 // 00000000 68 73 71 73 03 00 00 00 00 00 00 00 00 00 00 00 |hsqs............| 

158 // 00000010 00 00 00 00 00 00 00 00 00 00 00 00 02 00 00 00 |................| 

159 // squashfs_v2_magic_le 

160 68 73 71 73 [24] 02 00 

161 """ 

162 ), 

163 ] 

164 

165 DOC = HandlerDoc( 

166 name="SquashFS (v2)", 

167 description="SquashFS version 2 is a compressed, read-only file system format designed for minimal storage usage. It builds upon version 1 with additional features and improvements for embedded systems and Linux distributions.", 

168 handler_type=HandlerType.FILESYSTEM, 

169 vendor=None, 

170 references=[ 

171 Reference( 

172 title="SquashFS Documentation", 

173 url="https://dr-emann.github.io/squashfs/", 

174 ), 

175 Reference( 

176 title="SquashFS Wikipedia", 

177 url="https://en.wikipedia.org/wiki/SquashFS", 

178 ), 

179 ], 

180 limitations=[], 

181 ) 

182 

183 

184class SquashFSv3Handler(_SquashFSBase): 

185 NAME = "squashfs_v3" 

186 

187 PATTERNS = [ 

188 HexString( 

189 """ 

190 // 00000000 73 71 73 68 00 00 00 03 00 00 00 00 00 00 00 00 |sqsh............| 

191 // 00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 03 00 00 |................| 

192 // squashfs_v3_magic_be 

193 73 71 73 68 [24] 00 03 

194 """ 

195 ), 

196 HexString( 

197 """ 

198 // 00000000 68 73 71 73 03 00 00 00 00 00 00 00 00 00 00 00 |hsqs............| 

199 // 00000010 00 00 00 00 00 00 00 00 00 00 00 00 03 00 00 00 |................| 

200 // squashfs_v3_magic_le 

201 68 73 71 73 [24] 03 00 

202 """ 

203 ), 

204 ] 

205 

206 DOC = HandlerDoc( 

207 name="SquashFS (v3)", 

208 description="SquashFS version 3 is a compressed, read-only file system format designed for minimal storage usage. It is widely used in embedded systems and Linux distributions for efficient storage and fast access.", 

209 handler_type=HandlerType.FILESYSTEM, 

210 vendor=None, 

211 references=[ 

212 Reference( 

213 title="SquashFS Documentation", 

214 url="https://dr-emann.github.io/squashfs/", 

215 ), 

216 Reference( 

217 title="SquashFS Wikipedia", 

218 url="https://en.wikipedia.org/wiki/SquashFS", 

219 ), 

220 ], 

221 limitations=[], 

222 ) 

223 

224 C_DEFINITIONS = r""" 

225 typedef struct squashfs3_super_block 

226 { 

227 char s_magic[4]; 

228 uint32 inodes; 

229 uint32 bytes_used_2; 

230 uint32 uid_start_2; 

231 uint32 guid_start_2; 

232 uint32 inode_table_start_2; 

233 uint32 directory_table_start_2; 

234 uint16 s_major; 

235 uint16 s_minor; 

236 uint16 block_size_1; 

237 uint16 block_log; 

238 uint8 flags; 

239 uint8 no_uids; 

240 uint8 no_guids; 

241 uint32 mkfs_time /* time of filesystem creation */; 

242 uint64 root_inode; 

243 uint32 block_size; 

244 uint32 fragments; 

245 uint32 fragment_table_start_2; 

246 uint64 bytes_used; 

247 uint64 uid_start; 

248 uint64 guid_start; 

249 uint64 inode_table_start; 

250 uint64 directory_table_start; 

251 uint64 fragment_table_start; 

252 uint64 lookup_table_start; 

253 } squashfs3_super_block_t; 

254 """ 

255 HEADER_STRUCT = "squashfs3_super_block_t" 

256 

257 

258class SquashFSv3DDWRTHandler(SquashFSv3Handler): 

259 NAME = "squashfs_v3_ddwrt" 

260 

261 BIG_ENDIAN_MAGIC = 0x74_71_73_68 

262 

263 EXTRACTOR = SquashFSExtractor(0x74_71_73_68) 

264 

265 PATTERNS = [ 

266 HexString( 

267 """ 

268 // 00000000 68 73 71 74 21 02 00 00 00 00 00 00 00 00 00 00 |hsqt!...........| 

269 // 00000010 00 00 00 00 00 00 00 00 50 02 00 00 03 00 00 00 |........P.......| 

270 // squashfs_v3_magic_ddwrt_le 

271 68 73 71 74 [24] 03 00 

272 """ 

273 ), 

274 HexString( 

275 """ 

276 // 00000000 74 71 73 68 00 00 00 05 00 00 00 00 00 00 00 00 |tqsh............| 

277 // 00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 03 00 01 |................| 

278 // squashfs_v3_magic_ddwrt_be 

279 74 71 73 68 [24] 00 03 

280 """ 

281 ), 

282 ] 

283 

284 DOC = HandlerDoc( 

285 name="SquashFS (v3-DDWRT)", 

286 description="SquashFS version 3 DD-WRT is a variant of the SquashFS v3 format used in DD-WRT firmware. It features a unique magic number and may include specific optimizations for embedded systems.", 

287 handler_type=HandlerType.FILESYSTEM, 

288 vendor="DDWRT", 

289 references=[ 

290 Reference( 

291 title="SquashFS Documentation", 

292 url="https://dr-emann.github.io/squashfs/", 

293 ), 

294 Reference( 

295 title="SquashFS Wikipedia", 

296 url="https://en.wikipedia.org/wiki/SquashFS", 

297 ), 

298 ], 

299 limitations=[], 

300 ) 

301 

302 

303class SquashFSv3BroadcomHandler(SquashFSv3Handler): 

304 NAME = "squashfs_v3_broadcom" 

305 

306 BIG_ENDIAN_MAGIC = 0x71_73_68_73 

307 

308 EXTRACTOR = SquashFSExtractor(0x71_73_68_73) 

309 

310 PATTERNS = [ 

311 HexString( 

312 """ 

313 // 00000000 73 68 73 71 0f 05 00 00 c8 a9 00 01 00 00 00 bc |shsq............| 

314 // 00000010 1f 2d 00 a2 d0 2b 00 bf 79 2e 00 65 03 00 00 00 |.-...+..y..e....| 

315 // squashfs_v3_magic_broadcom_le 

316 73 68 73 71 [24] 03 00 

317 """ 

318 ), 

319 HexString( 

320 """ 

321 // 00000000 71 73 68 73 0f 05 00 00 c8 a9 00 01 00 00 00 bc |qshs............| 

322 // 00000010 1f 2d 00 a2 d0 2b 00 bf 79 2e 00 65 00 03 00 00 |.-...+..y..e....| 

323 // squashfs_v3_magic_broadcom_be 

324 71 73 68 73 [24] 00 03 

325 

326 """ 

327 ), 

328 ] 

329 

330 DOC = HandlerDoc( 

331 name="SquashFS (v3-Broadcom)", 

332 description="SquashFS version 3 Broadcom is a variant of the SquashFS v3 format used in Broadcom firmware. It features a unique magic number and may include specific optimizations for Broadcom devices.", 

333 handler_type=HandlerType.FILESYSTEM, 

334 vendor="Broadcom", 

335 references=[ 

336 Reference( 

337 title="SquashFS Documentation", 

338 url="https://dr-emann.github.io/squashfs/", 

339 ), 

340 Reference( 

341 title="SquashFS Wikipedia", 

342 url="https://en.wikipedia.org/wiki/SquashFS", 

343 ), 

344 ], 

345 limitations=[], 

346 ) 

347 

348 

349class SquashFSv3NSHandler(SquashFSv3Handler): 

350 NAME = "squashfs_v3_nonstandard" 

351 

352 BIG_ENDIAN_MAGIC = 0x73_71_6C_7A 

353 

354 EXTRACTOR = SquashFSExtractor(0x73_71_6C_7A) 

355 

356 PATTERNS = [ 

357 HexString( 

358 """ 

359 // 00000000 7a 6c 71 73 00 00 04 df 57 00 17 95 46 00 19 ed |zlqs....W...F...| 

360 // 00000010 20 08 04 8e 02 40 01 06 c9 02 16 00 00 03 00 01 | ....@..........| 

361 // squashfs_v3_magic_nonstandard_le 

362 7A 6c 71 73 [24] 03 00 

363 """ 

364 ), 

365 HexString( 

366 """ 

367 // 00000000 73 71 6c 7a 00 00 04 df 57 00 17 95 46 00 19 ed |sqlz....W...F...| 

368 // 00000010 20 08 04 8e 02 40 01 06 c9 02 16 00 00 03 00 01 | ....@..........| 

369 // squashfs_v3_magic_nonstandard_be 

370 73 71 6c 7A [24] 00 03 

371 """ 

372 ), 

373 ] 

374 

375 DOC = HandlerDoc( 

376 name="SquashFS (v3-non-standard)", 

377 description="SquashFS version 3 is a compressed, read-only file system format designed for minimal storage usage. It is widely used in embedded systems and Linux distributions for efficient storage and fast access.", 

378 handler_type=HandlerType.FILESYSTEM, 

379 vendor="unknown", 

380 references=[ 

381 Reference( 

382 title="SquashFS Documentation", 

383 url="https://dr-emann.github.io/squashfs/", 

384 ), 

385 Reference( 

386 title="SquashFS Wikipedia", 

387 url="https://en.wikipedia.org/wiki/SquashFS", 

388 ), 

389 ], 

390 limitations=[], 

391 ) 

392 

393 

394class SquashFSv4LEHandler(_SquashFSBase): 

395 NAME = "squashfs_v4_le" 

396 

397 PATTERNS = [ 

398 HexString( 

399 """ 

400 // 00000000 68 73 71 73 03 00 00 00 00 c1 9c 61 00 00 02 00 |hsqs.......a....| 

401 // 00000010 01 00 00 00 01 00 11 00 c0 00 01 00 04 00 00 00 |................| 

402 // squashfs_v4_magic_le 

403 68 73 71 73 [24] 04 00 

404 """ 

405 ), 

406 ] 

407 

408 C_DEFINITIONS = r""" 

409 typedef struct squashfs4_super_block 

410 { 

411 char s_magic[4]; 

412 uint32 inodes; 

413 uint32 mkfs_time /* time of filesystem creation */; 

414 uint32 block_size; 

415 uint32 fragments; 

416 uint16 compression; 

417 uint16 block_log; 

418 uint16 flags; 

419 uint16 no_ids; 

420 uint16 s_major; 

421 uint16 s_minor; 

422 uint64 root_inode; 

423 uint64 bytes_used; 

424 uint64 id_table_start; 

425 uint64 xattr_id_table_start; 

426 uint64 inode_table_start; 

427 uint64 directory_table_start; 

428 uint64 fragment_table_start; 

429 uint64 lookup_table_start; 

430 } squashfs4_super_block_t; 

431 """ 

432 HEADER_STRUCT = "squashfs4_super_block_t" 

433 

434 DOC = HandlerDoc( 

435 name="SquashFS (v4-LE)", 

436 description="SquashFS version 4 is a compressed, read-only file system format designed for minimal storage usage and fast access. It is widely used in embedded systems and Linux distributions for efficient storage management.", 

437 handler_type=HandlerType.FILESYSTEM, 

438 vendor=None, 

439 references=[ 

440 Reference( 

441 title="SquashFS Documentation", 

442 url="https://dr-emann.github.io/squashfs/", 

443 ), 

444 Reference( 

445 title="SquashFS Wikipedia", 

446 url="https://en.wikipedia.org/wiki/SquashFS", 

447 ), 

448 ], 

449 limitations=[], 

450 ) 

451 

452 

453class SquashFSv4BEExtractor(Extractor): 

454 EXECUTABLE = "sasquatch-v4be" 

455 

456 def is_avm(self, header) -> bool: 

457 # see https://raw.githubusercontent.com/Freetz/freetz/master/tools/make/squashfs4-host-be/AVM-BE-format.txt 

458 return header.bytes_used == header.mkfs_time 

459 

460 def extract(self, inpath: Path, outdir: Path): 

461 struct_parser = StructParser(SquashFSv4LEHandler.C_DEFINITIONS) 

462 

463 with File.from_path(inpath) as f: 

464 header = struct_parser.parse( 

465 SquashFSv4LEHandler.HEADER_STRUCT, f, Endian.BIG 

466 ) 

467 

468 commands_args = [] 

469 

470 if not self.is_avm(header): 

471 commands_args.append("-be") 

472 

473 commands_args.extend( 

474 [ 

475 "-no-exit-code", 

476 "-f", 

477 "-d", 

478 "{outdir}", 

479 "{inpath}", 

480 ] 

481 ) 

482 extractor = Command(self.EXECUTABLE, *commands_args) 

483 extractor.extract(inpath, outdir) 

484 

485 def get_dependencies(self) -> list[str]: 

486 return [self.EXECUTABLE] 

487 

488 

489class SquashFSv4BEHandler(SquashFSv4LEHandler): 

490 NAME = "squashfs_v4_be" 

491 

492 PATTERNS = [ 

493 HexString( 

494 """ 

495 // 00000000 73 71 73 68 03 00 00 00 00 c1 9c 61 00 00 02 00 |sqsh.......a....| 

496 // 00000010 01 00 00 00 01 00 11 00 c0 00 01 00 04 00 00 00 |................| 

497 // squashfs_v4_magic_be 

498 73 71 73 68 [24] 00 04 

499 """ 

500 ) 

501 ] 

502 

503 EXTRACTOR = SquashFSv4BEExtractor() 

504 

505 DOC = HandlerDoc( 

506 name="SquashFS (v4-BE)", 

507 description="SquashFS version 4 is a compressed, read-only file system format designed for minimal storage usage and fast access. It supports both big-endian and little-endian formats and is widely used in embedded systems and Linux distributions.", 

508 handler_type=HandlerType.FILESYSTEM, 

509 vendor=None, 

510 references=[ 

511 Reference( 

512 title="SquashFS Documentation", 

513 url="https://dr-emann.github.io/squashfs/", 

514 ), 

515 Reference( 

516 title="SquashFS Wikipedia", 

517 url="https://en.wikipedia.org/wiki/SquashFS", 

518 ), 

519 ], 

520 limitations=[], 

521 )