blob: 47537caf6b63f8f25020b5571021f6eaea42f31f [file] [log] [blame]
Elliott Hughes726a6a92021-08-17 15:02:00 -07001#! /usr/bin/env python3
Colin Cross28fa5bc2012-05-20 13:28:05 -07002
3# Copyright (C) 2012 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
Patrick Tjinc38720a2016-10-03 13:03:23 -070017import csv
18import getopt
19import hashlib
20import posixpath
21import signal
22import struct
23import sys
24
Colin Cross28fa5bc2012-05-20 13:28:05 -070025
26def usage(argv0):
27 print("""
Patrick Tjinc38720a2016-10-03 13:03:23 -070028Usage: %s [-v] [-s] [-c <filename>] sparse_image_file ...
Colin Cross28fa5bc2012-05-20 13:28:05 -070029 -v verbose output
Patrick Tjinc38720a2016-10-03 13:03:23 -070030 -s show sha1sum of data blocks
31 -c <filename> save .csv file of blocks
32""" % (argv0))
Colin Cross28fa5bc2012-05-20 13:28:05 -070033 sys.exit(2)
34
Colin Cross28fa5bc2012-05-20 13:28:05 -070035
Patrick Tjinc38720a2016-10-03 13:03:23 -070036def main():
Colin Cross28fa5bc2012-05-20 13:28:05 -070037 signal.signal(signal.SIGPIPE, signal.SIG_DFL)
38
39 me = posixpath.basename(sys.argv[0])
40
41 # Parse the command line
Patrick Tjinc38720a2016-10-03 13:03:23 -070042 verbose = 0 # -v
43 showhash = 0 # -s
44 csvfilename = None # -c
Colin Cross28fa5bc2012-05-20 13:28:05 -070045 try:
46 opts, args = getopt.getopt(sys.argv[1:],
Patrick Tjinc38720a2016-10-03 13:03:23 -070047 "vsc:",
48 ["verbose", "showhash", "csvfile"])
Elliott Hughes726a6a92021-08-17 15:02:00 -070049 except getopt.GetoptError as e:
Colin Cross28fa5bc2012-05-20 13:28:05 -070050 print(e)
51 usage(me)
52 for o, a in opts:
53 if o in ("-v", "--verbose"):
54 verbose += 1
Patrick Tjinc38720a2016-10-03 13:03:23 -070055 elif o in ("-s", "--showhash"):
56 showhash = True
57 elif o in ("-c", "--csvfile"):
58 csvfilename = a
Colin Cross28fa5bc2012-05-20 13:28:05 -070059 else:
60 print("Unrecognized option \"%s\"" % (o))
61 usage(me)
62
Patrick Tjinc38720a2016-10-03 13:03:23 -070063 if not args:
Colin Cross28fa5bc2012-05-20 13:28:05 -070064 print("No sparse_image_file specified")
65 usage(me)
66
Patrick Tjinc38720a2016-10-03 13:03:23 -070067 if csvfilename:
68 csvfile = open(csvfilename, "wb")
69 csvwriter = csv.writer(csvfile)
70
71 output = verbose or csvfilename or showhash
72
Colin Cross28fa5bc2012-05-20 13:28:05 -070073 for path in args:
Patrick Tjinc38720a2016-10-03 13:03:23 -070074 FH = open(path, "rb")
Colin Cross28fa5bc2012-05-20 13:28:05 -070075 header_bin = FH.read(28)
76 header = struct.unpack("<I4H4I", header_bin)
77
78 magic = header[0]
79 major_version = header[1]
80 minor_version = header[2]
81 file_hdr_sz = header[3]
82 chunk_hdr_sz = header[4]
83 blk_sz = header[5]
84 total_blks = header[6]
85 total_chunks = header[7]
86 image_checksum = header[8]
87
88 if magic != 0xED26FF3A:
89 print("%s: %s: Magic should be 0xED26FF3A but is 0x%08X"
90 % (me, path, magic))
91 continue
92 if major_version != 1 or minor_version != 0:
93 print("%s: %s: I only know about version 1.0, but this is version %u.%u"
94 % (me, path, major_version, minor_version))
95 continue
96 if file_hdr_sz != 28:
97 print("%s: %s: The file header size was expected to be 28, but is %u."
98 % (me, path, file_hdr_sz))
99 continue
100 if chunk_hdr_sz != 12:
101 print("%s: %s: The chunk header size was expected to be 12, but is %u."
102 % (me, path, chunk_hdr_sz))
103 continue
104
105 print("%s: Total of %u %u-byte output blocks in %u input chunks."
106 % (path, total_blks, blk_sz, total_chunks))
107
108 if image_checksum != 0:
109 print("checksum=0x%08X" % (image_checksum))
110
Patrick Tjinc38720a2016-10-03 13:03:23 -0700111 if not output:
Colin Cross28fa5bc2012-05-20 13:28:05 -0700112 continue
Patrick Tjinc38720a2016-10-03 13:03:23 -0700113
114 if verbose > 0:
115 print(" input_bytes output_blocks")
116 print("chunk offset number offset number")
117
118 if csvfilename:
119 csvwriter.writerow(["chunk", "input offset", "input bytes",
120 "output offset", "output blocks", "type", "hash"])
121
Colin Cross28fa5bc2012-05-20 13:28:05 -0700122 offset = 0
Elliott Hughesf75518e2022-01-18 17:26:56 -0800123 for i in range(1, total_chunks + 1):
Colin Cross28fa5bc2012-05-20 13:28:05 -0700124 header_bin = FH.read(12)
125 header = struct.unpack("<2H2I", header_bin)
126 chunk_type = header[0]
Colin Cross28fa5bc2012-05-20 13:28:05 -0700127 chunk_sz = header[2]
128 total_sz = header[3]
129 data_sz = total_sz - 12
Patrick Tjinc38720a2016-10-03 13:03:23 -0700130 curhash = ""
131 curtype = ""
132 curpos = FH.tell()
Colin Cross28fa5bc2012-05-20 13:28:05 -0700133
Patrick Tjinc38720a2016-10-03 13:03:23 -0700134 if verbose > 0:
135 print("%4u %10u %10u %7u %7u" % (i, curpos, data_sz, offset, chunk_sz),
136 end=" ")
Colin Cross28fa5bc2012-05-20 13:28:05 -0700137
138 if chunk_type == 0xCAC1:
139 if data_sz != (chunk_sz * blk_sz):
140 print("Raw chunk input size (%u) does not match output size (%u)"
141 % (data_sz, chunk_sz * blk_sz))
Patrick Tjinc38720a2016-10-03 13:03:23 -0700142 break
Colin Cross28fa5bc2012-05-20 13:28:05 -0700143 else:
Patrick Tjinc38720a2016-10-03 13:03:23 -0700144 curtype = "Raw data"
145 data = FH.read(data_sz)
146 if showhash:
147 h = hashlib.sha1()
148 h.update(data)
149 curhash = h.hexdigest()
Colin Cross28fa5bc2012-05-20 13:28:05 -0700150 elif chunk_type == 0xCAC2:
151 if data_sz != 4:
152 print("Fill chunk should have 4 bytes of fill, but this has %u"
Patrick Tjinc38720a2016-10-03 13:03:23 -0700153 % (data_sz))
154 break
Colin Cross28fa5bc2012-05-20 13:28:05 -0700155 else:
156 fill_bin = FH.read(4)
157 fill = struct.unpack("<I", fill_bin)
Patrick Tjinc38720a2016-10-03 13:03:23 -0700158 curtype = format("Fill with 0x%08X" % (fill))
159 if showhash:
160 h = hashlib.sha1()
Bowgo Tsaidbf62d92022-10-03 16:15:21 +0800161 data = fill_bin * (blk_sz // 4);
Elliott Hughesf75518e2022-01-18 17:26:56 -0800162 for block in range(chunk_sz):
Patrick Tjinc38720a2016-10-03 13:03:23 -0700163 h.update(data)
164 curhash = h.hexdigest()
Colin Cross28fa5bc2012-05-20 13:28:05 -0700165 elif chunk_type == 0xCAC3:
166 if data_sz != 0:
167 print("Don't care chunk input size is non-zero (%u)" % (data_sz))
Patrick Tjinc38720a2016-10-03 13:03:23 -0700168 break
Colin Cross28fa5bc2012-05-20 13:28:05 -0700169 else:
Patrick Tjinc38720a2016-10-03 13:03:23 -0700170 curtype = "Don't care"
Colin Cross28fa5bc2012-05-20 13:28:05 -0700171 elif chunk_type == 0xCAC4:
172 if data_sz != 4:
173 print("CRC32 chunk should have 4 bytes of CRC, but this has %u"
Patrick Tjinc38720a2016-10-03 13:03:23 -0700174 % (data_sz))
175 break
Colin Cross28fa5bc2012-05-20 13:28:05 -0700176 else:
177 crc_bin = FH.read(4)
Eric Miao4cc39782015-04-12 16:31:46 -0700178 crc = struct.unpack("<I", crc_bin)
Patrick Tjinc38720a2016-10-03 13:03:23 -0700179 curtype = format("Unverified CRC32 0x%08X" % (crc))
Colin Cross28fa5bc2012-05-20 13:28:05 -0700180 else:
Patrick Tjinc38720a2016-10-03 13:03:23 -0700181 print("Unknown chunk type 0x%04X" % (chunk_type))
182 break
Colin Cross28fa5bc2012-05-20 13:28:05 -0700183
Patrick Tjinc38720a2016-10-03 13:03:23 -0700184 if verbose > 0:
185 print("%-18s" % (curtype), end=" ")
186
187 if verbose > 1:
188 header = struct.unpack("<12B", header_bin)
189 print(" (%02X%02X %02X%02X %02X%02X%02X%02X %02X%02X%02X%02X)"
190 % (header[0], header[1], header[2], header[3],
191 header[4], header[5], header[6], header[7],
192 header[8], header[9], header[10], header[11]), end=" ")
193
194 print(curhash)
195
196 if csvfilename:
197 csvwriter.writerow([i, curpos, data_sz, offset, chunk_sz, curtype,
198 curhash])
Colin Cross28fa5bc2012-05-20 13:28:05 -0700199
200 offset += chunk_sz
201
Patrick Tjinc38720a2016-10-03 13:03:23 -0700202 if verbose > 0:
203 print(" %10u %7u End" % (FH.tell(), offset))
Colin Cross28fa5bc2012-05-20 13:28:05 -0700204
205 if total_blks != offset:
206 print("The header said we should have %u output blocks, but we saw %u"
207 % (total_blks, offset))
208
209 junk_len = len(FH.read())
210 if junk_len:
211 print("There were %u bytes of extra data at the end of the file."
212 % (junk_len))
213
Patrick Tjinc38720a2016-10-03 13:03:23 -0700214 if csvfilename:
215 csvfile.close()
216
Colin Cross28fa5bc2012-05-20 13:28:05 -0700217 sys.exit(0)
218
219if __name__ == "__main__":
220 main()