xref: /aosp_15_r20/system/update_engine/scripts/scan_lz4.py (revision 5a9231315b4521097b8dc3750bc806fcafe0c72f)
1# import mmap
2
3import struct
4
5LZ4_FRAME_MAGIC = b"\x04\x22\x4D\x18"
6
7
8def scan_legacy_lz4_frames(data):
9  LZ4_LEGACY_FRAME_MAGIC = b"\x02\x21\x4C\x18"
10  index = 0
11  while index < len(data):
12    try:
13      index = data.index(LZ4_LEGACY_FRAME_MAGIC, index)
14      print("Legacy Lz4 frame at {}".format(index))
15      index += 4
16      while index < len(data):
17        magic = data[index:index+4]
18        if magic == LZ4_LEGACY_FRAME_MAGIC or magic == LZ4_FRAME_MAGIC:
19          break
20        (csize,) = struct.unpack("<L", magic)
21        if index + 4 + csize >= len(data) or csize == 0:
22          break
23        print("Legacy lz4 block at {}, compressed data size {}".format(index, csize))
24        index += csize
25
26    except ValueError:
27      break
28
29
30def scan_lz4_frames(data):
31  index = 0
32  while index < len(data):
33    try:
34      index = data.index(LZ4_FRAME_MAGIC, index)
35      frame_offset = index
36      index += 4
37      flag = data[index]
38      block_descriptor = data[index+1]
39      block_checksum_present = flag & 0x10 != 0
40      content_size_present = flag & 0x8 != 0
41      content_checksum_present = flag & 0x4 != 0
42      dictionary_id = flag & 0x1 != 0
43      index += 2
44      content_size = None
45      if content_size_present:
46        content_size = struct.unpack("<Q", data[index:index+8])
47        index += 8
48      if dictionary_id:
49        dictionary_id = struct.unpack("<L", data[index:index+4])
50        index += 4
51      header_checksum = data[index:index+1]
52      index += 1
53      print("Lz4 frame at {}, content size: {}".format(
54          frame_offset, content_size))
55      while index < len(data):
56        (block_size,) = struct.unpack("<L", data[index:index+4])
57        uncompressed = block_size & 0x80000000 != 0
58        block_size &= 0x7FFFFFFF
59        index += 4
60        index += block_size
61        if index >= len(data) or block_size == 0:
62          break
63        print("Block uncompressed: {}, size: {}".format(uncompressed, block_size))
64    except ValueError:
65      break
66
67
68def main(argv):
69  if len(argv) != 2:
70    print("Usage:", argv[0], "<path to a file>")
71    return 1
72  path = argv[1]
73
74  with open(path, "rb") as fp:
75    data = fp.read()
76    scan_legacy_lz4_frames(data)
77    scan_lz4_frames(data)
78
79
80if __name__ == '__main__':
81  import sys
82  sys.exit(main(sys.argv))
83