blob: 0ad079841e6430410b1adae11ed8f43774aac76a [file] [log] [blame]
Tao Baoafaa0a62017-02-27 15:08:36 -08001#!/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)
Tao Baod8617142017-08-30 15:54:59 -070074 unpacked_file = common.File(entry, file_data)
75 file_size = unpacked_file.size
Tao Baoafaa0a62017-02-27 15:08:36 -080076
Tao Baod8617142017-08-30 15:54:59 -070077 # block.map may contain less blocks, because mke2fs may skip allocating
78 # blocks if they contain all zeros. We can't reconstruct such a file from
79 # its block list. (Bug: 65213616)
80 if file_size > ranges.size() * 4096:
81 logging.warning(
82 'Skipping %s that has less blocks: file size %d-byte,'
83 ' ranges %s (%d-byte)', entry, file_size, ranges,
84 ranges.size() * 4096)
85 continue
86
87 file_sha1 = unpacked_file.sha1
Tao Baoafaa0a62017-02-27 15:08:36 -080088 assert blocks_sha1 == file_sha1, \
89 'file: %s, range: %s, blocks_sha1: %s, file_sha1: %s' % (
90 entry, ranges, blocks_sha1, file_sha1)
91
92 logging.info('Validating file consistency.')
93
94 # Verify IMAGES/system.img.
95 CheckAllFiles('system')
96
97 # Verify IMAGES/vendor.img if applicable.
98 if 'VENDOR/' in input_zip.namelist():
99 CheckAllFiles('vendor')
100
101 # Not checking IMAGES/system_other.img since it doesn't have the map file.
102
103
104def main(argv):
105 def option_handler():
106 return True
107
108 args = common.ParseOptions(
109 argv, __doc__, extra_opts="",
110 extra_long_opts=[],
111 extra_option_handler=option_handler)
112
113 if len(args) != 1:
114 common.Usage(__doc__)
115 sys.exit(1)
116
117 logging_format = '%(asctime)s - %(filename)s - %(levelname)-8s: %(message)s'
118 date_format = '%Y/%m/%d %H:%M:%S'
119 logging.basicConfig(level=logging.INFO, format=logging_format,
120 datefmt=date_format)
121
122 logging.info("Unzipping the input target_files.zip: %s", args[0])
123 input_tmp, input_zip = common.UnzipTemp(args[0])
124
125 ValidateFileConsistency(input_zip, input_tmp)
126
127 # TODO: Check if the OTA keys have been properly updated (the ones on /system,
128 # in recovery image).
129
130 # TODO(b/35411009): Verify the contents in /system/bin/install-recovery.sh.
131
132 logging.info("Done.")
133
134
135if __name__ == '__main__':
136 try:
137 main(sys.argv[1:])
138 finally:
139 common.Cleanup()