1 #! /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 17 from __future__ import print_function 18 import getopt, posixpath, signal, struct, sys 19 20 def usage(argv0): 21 print(""" 22 Usage: %s [-v] sparse_image_file ... 23 -v verbose output 24 """ % ( argv0 )) 25 sys.exit(2) 26 27 def main(): 28 29 signal.signal(signal.SIGPIPE, signal.SIG_DFL) 30 31 me = posixpath.basename(sys.argv[0]) 32 33 # Parse the command line 34 verbose = 0 # -v 35 try: 36 opts, args = getopt.getopt(sys.argv[1:], 37 "v", 38 ["verbose"]) 39 except getopt.GetoptError, e: 40 print(e) 41 usage(me) 42 for o, a in opts: 43 if o in ("-v", "--verbose"): 44 verbose += 1 45 else: 46 print("Unrecognized option \"%s\"" % (o)) 47 usage(me) 48 49 if len(args) == 0: 50 print("No sparse_image_file specified") 51 usage(me) 52 53 for path in args: 54 FH = open(path, 'rb') 55 header_bin = FH.read(28) 56 header = struct.unpack("<I4H4I", header_bin) 57 58 magic = header[0] 59 major_version = header[1] 60 minor_version = header[2] 61 file_hdr_sz = header[3] 62 chunk_hdr_sz = header[4] 63 blk_sz = header[5] 64 total_blks = header[6] 65 total_chunks = header[7] 66 image_checksum = header[8] 67 68 if magic != 0xED26FF3A: 69 print("%s: %s: Magic should be 0xED26FF3A but is 0x%08X" 70 % (me, path, magic)) 71 continue 72 if major_version != 1 or minor_version != 0: 73 print("%s: %s: I only know about version 1.0, but this is version %u.%u" 74 % (me, path, major_version, minor_version)) 75 continue 76 if file_hdr_sz != 28: 77 print("%s: %s: The file header size was expected to be 28, but is %u." 78 % (me, path, file_hdr_sz)) 79 continue 80 if chunk_hdr_sz != 12: 81 print("%s: %s: The chunk header size was expected to be 12, but is %u." 82 % (me, path, chunk_hdr_sz)) 83 continue 84 85 print("%s: Total of %u %u-byte output blocks in %u input chunks." 86 % (path, total_blks, blk_sz, total_chunks)) 87 88 if image_checksum != 0: 89 print("checksum=0x%08X" % (image_checksum)) 90 91 if not verbose: 92 continue 93 print(" input_bytes output_blocks") 94 print("chunk offset number offset number") 95 offset = 0 96 for i in xrange(1,total_chunks+1): 97 header_bin = FH.read(12) 98 header = struct.unpack("<2H2I", header_bin) 99 chunk_type = header[0] 100 reserved1 = header[1] 101 chunk_sz = header[2] 102 total_sz = header[3] 103 data_sz = total_sz - 12 104 105 print("%4u %10u %10u %7u %7u" % (i, FH.tell(), data_sz, offset, chunk_sz), 106 end=" ") 107 108 if chunk_type == 0xCAC1: 109 if data_sz != (chunk_sz * blk_sz): 110 print("Raw chunk input size (%u) does not match output size (%u)" 111 % (data_sz, chunk_sz * blk_sz)) 112 break; 113 else: 114 print("Raw data", end="") 115 FH.read(data_sz) 116 elif chunk_type == 0xCAC2: 117 if data_sz != 4: 118 print("Fill chunk should have 4 bytes of fill, but this has %u" 119 % (data_sz), end="") 120 break; 121 else: 122 fill_bin = FH.read(4) 123 fill = struct.unpack("<I", fill_bin) 124 print("Fill with 0x%08X" % (fill)) 125 elif chunk_type == 0xCAC3: 126 if data_sz != 0: 127 print("Don't care chunk input size is non-zero (%u)" % (data_sz)) 128 break; 129 else: 130 print("Don't care", end="") 131 elif chunk_type == 0xCAC4: 132 if data_sz != 4: 133 print("CRC32 chunk should have 4 bytes of CRC, but this has %u" 134 % (data_sz), end="") 135 break; 136 else: 137 crc_bin = FH.read(4) 138 crc = struct.unpack("<I", crc_bin) 139 print("Unverified CRC32 0x%08X" % (crc)) 140 else: 141 print("Unknown chunk type 0x%04X" % (chunk_type), end="") 142 break; 143 144 if verbose > 1: 145 header = struct.unpack("<12B", header_bin) 146 print(" (%02X%02X %02X%02X %02X%02X%02X%02X %02X%02X%02X%02X)" 147 % (header[0], header[1], header[2], header[3], 148 header[4], header[5], header[6], header[7], 149 header[8], header[9], header[10], header[11])) 150 else: 151 print() 152 153 offset += chunk_sz 154 155 print(" %10u %7u End" % (FH.tell(), offset)) 156 157 if total_blks != offset: 158 print("The header said we should have %u output blocks, but we saw %u" 159 % (total_blks, offset)) 160 161 junk_len = len(FH.read()) 162 if junk_len: 163 print("There were %u bytes of extra data at the end of the file." 164 % (junk_len)) 165 166 sys.exit(0) 167 168 if __name__ == "__main__": 169 main() 170