validate_target_files.py revision afaa0a638b901ff2b474214caa65f136421abbae
1#!/usr/bin/env python 2 3# Copyright (C) 2017 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""" 18Validate a given (signed) target_files.zip. 19 20It performs checks to ensure the integrity of the input zip. 21 - It verifies the file consistency between the ones in IMAGES/system.img (read 22 via IMAGES/system.map) and the ones under unpacked folder of SYSTEM/. The 23 same check also applies to the vendor image if present. 24""" 25 26import common 27import logging 28import os.path 29import sparse_img 30import sys 31 32 33def _GetImage(which, tmpdir): 34 assert which in ('system', 'vendor') 35 36 path = os.path.join(tmpdir, 'IMAGES', which + '.img') 37 mappath = os.path.join(tmpdir, 'IMAGES', which + '.map') 38 39 # Map file must exist (allowed to be empty). 40 assert os.path.exists(path) and os.path.exists(mappath) 41 42 clobbered_blocks = '0' 43 return sparse_img.SparseImage(path, mappath, clobbered_blocks) 44 45 46def ValidateFileConsistency(input_zip, input_tmp): 47 """Compare the files from image files and unpacked folders.""" 48 49 def RoundUpTo4K(value): 50 rounded_up = value + 4095 51 return rounded_up - (rounded_up % 4096) 52 53 def CheckAllFiles(which): 54 logging.info('Checking %s image.', which) 55 image = _GetImage(which, input_tmp) 56 prefix = '/' + which 57 for entry in image.file_map: 58 if not entry.startswith(prefix): 59 continue 60 61 # Read the blocks that the file resides. Note that it will contain the 62 # bytes past the file length, which is expected to be padded with '\0's. 63 ranges = image.file_map[entry] 64 blocks_sha1 = image.RangeSha1(ranges) 65 66 # The filename under unpacked directory, such as SYSTEM/bin/sh. 67 unpacked_name = os.path.join( 68 input_tmp, which.upper(), entry[(len(prefix) + 1):]) 69 with open(unpacked_name) as f: 70 file_data = f.read() 71 file_size = len(file_data) 72 file_size_rounded_up = RoundUpTo4K(file_size) 73 file_data += '\0' * (file_size_rounded_up - file_size) 74 file_sha1 = common.File(entry, file_data).sha1 75 76 assert blocks_sha1 == file_sha1, \ 77 'file: %s, range: %s, blocks_sha1: %s, file_sha1: %s' % ( 78 entry, ranges, blocks_sha1, file_sha1) 79 80 logging.info('Validating file consistency.') 81 82 # Verify IMAGES/system.img. 83 CheckAllFiles('system') 84 85 # Verify IMAGES/vendor.img if applicable. 86 if 'VENDOR/' in input_zip.namelist(): 87 CheckAllFiles('vendor') 88 89 # Not checking IMAGES/system_other.img since it doesn't have the map file. 90 91 92def main(argv): 93 def option_handler(): 94 return True 95 96 args = common.ParseOptions( 97 argv, __doc__, extra_opts="", 98 extra_long_opts=[], 99 extra_option_handler=option_handler) 100 101 if len(args) != 1: 102 common.Usage(__doc__) 103 sys.exit(1) 104 105 logging_format = '%(asctime)s - %(filename)s - %(levelname)-8s: %(message)s' 106 date_format = '%Y/%m/%d %H:%M:%S' 107 logging.basicConfig(level=logging.INFO, format=logging_format, 108 datefmt=date_format) 109 110 logging.info("Unzipping the input target_files.zip: %s", args[0]) 111 input_tmp, input_zip = common.UnzipTemp(args[0]) 112 113 ValidateFileConsistency(input_zip, input_tmp) 114 115 # TODO: Check if the OTA keys have been properly updated (the ones on /system, 116 # in recovery image). 117 118 # TODO(b/35411009): Verify the contents in /system/bin/install-recovery.sh. 119 120 logging.info("Done.") 121 122 123if __name__ == '__main__': 124 try: 125 main(sys.argv[1:]) 126 finally: 127 common.Cleanup() 128