blob: 82a03ad955c0652ee8c389dad7138760f17d8451 [file] [log] [blame]
Colin Cross28fa5bc2012-05-20 13:28:05 -07001#! /usr/bin/env python
2
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
17from __future__ import print_function
Patrick Tjinc38720a2016-10-03 13:03:23 -070018import csv
19import getopt
20import hashlib
21import posixpath
22import signal
23import struct
24import sys
25
Colin Cross28fa5bc2012-05-20 13:28:05 -070026
27def usage(argv0):
28 print("""
Patrick Tjinc38720a2016-10-03 13:03:23 -070029Usage: %s [-v] [-s] [-c <filename>] sparse_image_file ...
Colin Cross28fa5bc2012-05-20 13:28:05 -070030 -v verbose output
Patrick Tjinc38720a2016-10-03 13:03:23 -070031 -s show sha1sum of data blocks
32 -c <filename> save .csv file of blocks
33""" % (argv0))
Colin Cross28fa5bc2012-05-20 13:28:05 -070034 sys.exit(2)
35
Colin Cross28fa5bc2012-05-20 13:28:05 -070036
Patrick Tjinc38720a2016-10-03 13:03:23 -070037def main():
Colin Cross28fa5bc2012-05-20 13:28:05 -070038 signal.signal(signal.SIGPIPE, signal.SIG_DFL)
39
40 me = posixpath.basename(sys.argv[0])
41
42 # Parse the command line
Patrick Tjinc38720a2016-10-03 13:03:23 -070043 verbose = 0 # -v
44 showhash = 0 # -s
45 csvfilename = None # -c
Colin Cross28fa5bc2012-05-20 13:28:05 -070046 try:
47 opts, args = getopt.getopt(sys.argv[1:],
Patrick Tjinc38720a2016-10-03 13:03:23 -070048 "vsc:",
49 ["verbose", "showhash", "csvfile"])
Colin Cross28fa5bc2012-05-20 13:28:05 -070050 except getopt.GetoptError, e:
51 print(e)
52 usage(me)
53 for o, a in opts:
54 if o in ("-v", "--verbose"):
55 verbose += 1
Patrick Tjinc38720a2016-10-03 13:03:23 -070056 elif o in ("-s", "--showhash"):
57 showhash = True
58 elif o in ("-c", "--csvfile"):
59 csvfilename = a
Colin Cross28fa5bc2012-05-20 13:28:05 -070060 else:
61 print("Unrecognized option \"%s\"" % (o))
62 usage(me)
63
Patrick Tjinc38720a2016-10-03 13:03:23 -070064 if not args:
Colin Cross28fa5bc2012-05-20 13:28:05 -070065 print("No sparse_image_file specified")
66 usage(me)
67
Patrick Tjinc38720a2016-10-03 13:03:23 -070068 if csvfilename:
69 csvfile = open(csvfilename, "wb")
70 csvwriter = csv.writer(csvfile)
71
72 output = verbose or csvfilename or showhash
73
Colin Cross28fa5bc2012-05-20 13:28:05 -070074 for path in args:
Patrick Tjinc38720a2016-10-03 13:03:23 -070075 FH = open(path, "rb")
Colin Cross28fa5bc2012-05-20 13:28:05 -070076 header_bin = FH.read(28)
77 header = struct.unpack("<I4H4I", header_bin)
78
79 magic = header[0]
80 major_version = header[1]
81 minor_version = header[2]
82 file_hdr_sz = header[3]
83 chunk_hdr_sz = header[4]
84 blk_sz = header[5]
85 total_blks = header[6]
86 total_chunks = header[7]
87 image_checksum = header[8]
88
89 if magic != 0xED26FF3A:
90 print("%s: %s: Magic should be 0xED26FF3A but is 0x%08X"
91 % (me, path, magic))
92 continue
93 if major_version != 1 or minor_version != 0:
94 print("%s: %s: I only know about version 1.0, but this is version %u.%u"
95 % (me, path, major_version, minor_version))
96 continue
97 if file_hdr_sz != 28:
98 print("%s: %s: The file header size was expected to be 28, but is %u."
99 % (me, path, file_hdr_sz))
100 continue
101 if chunk_hdr_sz != 12:
102 print("%s: %s: The chunk header size was expected to be 12, but is %u."
103 % (me, path, chunk_hdr_sz))
104 continue
105
106 print("%s: Total of %u %u-byte output blocks in %u input chunks."
107 % (path, total_blks, blk_sz, total_chunks))
108
109 if image_checksum != 0:
110 print("checksum=0x%08X" % (image_checksum))
111
Patrick Tjinc38720a2016-10-03 13:03:23 -0700112 if not output:
Colin Cross28fa5bc2012-05-20 13:28:05 -0700113 continue
Patrick Tjinc38720a2016-10-03 13:03:23 -0700114
115 if verbose > 0:
116 print(" input_bytes output_blocks")
117 print("chunk offset number offset number")
118
119 if csvfilename:
120 csvwriter.writerow(["chunk", "input offset", "input bytes",
121 "output offset", "output blocks", "type", "hash"])
122
Colin Cross28fa5bc2012-05-20 13:28:05 -0700123 offset = 0
Patrick Tjinc38720a2016-10-03 13:03:23 -0700124 for i in xrange(1, total_chunks + 1):
Colin Cross28fa5bc2012-05-20 13:28:05 -0700125 header_bin = FH.read(12)
126 header = struct.unpack("<2H2I", header_bin)
127 chunk_type = header[0]
Colin Cross28fa5bc2012-05-20 13:28:05 -0700128 chunk_sz = header[2]
129 total_sz = header[3]
130 data_sz = total_sz - 12
Patrick Tjinc38720a2016-10-03 13:03:23 -0700131 curhash = ""
132 curtype = ""
133 curpos = FH.tell()
Colin Cross28fa5bc2012-05-20 13:28:05 -0700134
Patrick Tjinc38720a2016-10-03 13:03:23 -0700135 if verbose > 0:
136 print("%4u %10u %10u %7u %7u" % (i, curpos, data_sz, offset, chunk_sz),
137 end=" ")
Colin Cross28fa5bc2012-05-20 13:28:05 -0700138
139 if chunk_type == 0xCAC1:
140 if data_sz != (chunk_sz * blk_sz):
141 print("Raw chunk input size (%u) does not match output size (%u)"
142 % (data_sz, chunk_sz * blk_sz))
Patrick Tjinc38720a2016-10-03 13:03:23 -0700143 break
Colin Cross28fa5bc2012-05-20 13:28:05 -0700144 else:
Patrick Tjinc38720a2016-10-03 13:03:23 -0700145 curtype = "Raw data"
146 data = FH.read(data_sz)
147 if showhash:
148 h = hashlib.sha1()
149 h.update(data)
150 curhash = h.hexdigest()
Colin Cross28fa5bc2012-05-20 13:28:05 -0700151 elif chunk_type == 0xCAC2:
152 if data_sz != 4:
153 print("Fill chunk should have 4 bytes of fill, but this has %u"
Patrick Tjinc38720a2016-10-03 13:03:23 -0700154 % (data_sz))
155 break
Colin Cross28fa5bc2012-05-20 13:28:05 -0700156 else:
157 fill_bin = FH.read(4)
158 fill = struct.unpack("<I", fill_bin)
Patrick Tjinc38720a2016-10-03 13:03:23 -0700159 curtype = format("Fill with 0x%08X" % (fill))
160 if showhash:
161 h = hashlib.sha1()
162 data = fill_bin * (blk_sz / 4);
163 for block in xrange(chunk_sz):
164 h.update(data)
165 curhash = h.hexdigest()
Colin Cross28fa5bc2012-05-20 13:28:05 -0700166 elif chunk_type == 0xCAC3:
167 if data_sz != 0:
168 print("Don't care chunk input size is non-zero (%u)" % (data_sz))
Patrick Tjinc38720a2016-10-03 13:03:23 -0700169 break
Colin Cross28fa5bc2012-05-20 13:28:05 -0700170 else:
Patrick Tjinc38720a2016-10-03 13:03:23 -0700171 curtype = "Don't care"
Colin Cross28fa5bc2012-05-20 13:28:05 -0700172 elif chunk_type == 0xCAC4:
173 if data_sz != 4:
174 print("CRC32 chunk should have 4 bytes of CRC, but this has %u"
Patrick Tjinc38720a2016-10-03 13:03:23 -0700175 % (data_sz))
176 break
Colin Cross28fa5bc2012-05-20 13:28:05 -0700177 else:
178 crc_bin = FH.read(4)
Eric Miao4cc39782015-04-12 16:31:46 -0700179 crc = struct.unpack("<I", crc_bin)
Patrick Tjinc38720a2016-10-03 13:03:23 -0700180 curtype = format("Unverified CRC32 0x%08X" % (crc))
Colin Cross28fa5bc2012-05-20 13:28:05 -0700181 else:
Patrick Tjinc38720a2016-10-03 13:03:23 -0700182 print("Unknown chunk type 0x%04X" % (chunk_type))
183 break
Colin Cross28fa5bc2012-05-20 13:28:05 -0700184
Patrick Tjinc38720a2016-10-03 13:03:23 -0700185 if verbose > 0:
186 print("%-18s" % (curtype), end=" ")
187
188 if verbose > 1:
189 header = struct.unpack("<12B", header_bin)
190 print(" (%02X%02X %02X%02X %02X%02X%02X%02X %02X%02X%02X%02X)"
191 % (header[0], header[1], header[2], header[3],
192 header[4], header[5], header[6], header[7],
193 header[8], header[9], header[10], header[11]), end=" ")
194
195 print(curhash)
196
197 if csvfilename:
198 csvwriter.writerow([i, curpos, data_sz, offset, chunk_sz, curtype,
199 curhash])
Colin Cross28fa5bc2012-05-20 13:28:05 -0700200
201 offset += chunk_sz
202
Patrick Tjinc38720a2016-10-03 13:03:23 -0700203 if verbose > 0:
204 print(" %10u %7u End" % (FH.tell(), offset))
Colin Cross28fa5bc2012-05-20 13:28:05 -0700205
206 if total_blks != offset:
207 print("The header said we should have %u output blocks, but we saw %u"
208 % (total_blks, offset))
209
210 junk_len = len(FH.read())
211 if junk_len:
212 print("There were %u bytes of extra data at the end of the file."
213 % (junk_len))
214
Patrick Tjinc38720a2016-10-03 13:03:23 -0700215 if csvfilename:
216 csvfile.close()
217
Colin Cross28fa5bc2012-05-20 13:28:05 -0700218 sys.exit(0)
219
220if __name__ == "__main__":
221 main()