add_img_to_target_files.py revision 12d87fc174d8c2f47a1f738f0d6514c2ceb4d241
1#!/usr/bin/env python 2# 3# Copyright (C) 2014 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""" 18Given a target-files zipfile that does not contain images (ie, does 19not have an IMAGES/ top-level subdirectory), produce the images and 20add them to the zipfile. 21 22Usage: add_img_to_target_files [flag] target_files 23 24 -a (--add_missing) 25 Build and add missing images to "IMAGES/". If this option is 26 not specified, this script will simply exit when "IMAGES/" 27 directory exists in the target file. 28 29 -r (--rebuild_recovery) 30 Rebuild the recovery patch and write it to the system image. Only 31 meaningful when system image needs to be rebuilt. 32 33 --replace_verity_private_key 34 Replace the private key used for verity signing. (same as the option 35 in sign_target_files_apks) 36 37 --replace_verity_public_key 38 Replace the certificate (public key) used for verity verification. (same 39 as the option in sign_target_files_apks) 40 41 --is_signing 42 Skip building & adding the images for "userdata" and "cache" if we 43 are signing the target files. 44""" 45 46from __future__ import print_function 47 48import datetime 49import os 50import shlex 51import shutil 52import subprocess 53import sys 54import uuid 55import zipfile 56 57import build_image 58import common 59import rangelib 60import sparse_img 61 62if sys.hexversion < 0x02070000: 63 print("Python 2.7 or newer is required.", file=sys.stderr) 64 sys.exit(1) 65 66OPTIONS = common.OPTIONS 67 68OPTIONS.add_missing = False 69OPTIONS.rebuild_recovery = False 70OPTIONS.replace_updated_files_list = [] 71OPTIONS.replace_verity_public_key = False 72OPTIONS.replace_verity_private_key = False 73OPTIONS.is_signing = False 74 75 76# Partitions that should have their care_map added to META/care_map.txt. 77PARTITIONS_WITH_CARE_MAP = ('system', 'vendor', 'product') 78 79 80class OutputFile(object): 81 def __init__(self, output_zip, input_dir, prefix, name): 82 self._output_zip = output_zip 83 self.input_name = os.path.join(input_dir, prefix, name) 84 85 if self._output_zip: 86 self._zip_name = os.path.join(prefix, name) 87 88 root, suffix = os.path.splitext(name) 89 self.name = common.MakeTempFile(prefix=root + '-', suffix=suffix) 90 else: 91 self.name = self.input_name 92 93 def Write(self): 94 if self._output_zip: 95 common.ZipWrite(self._output_zip, self.name, self._zip_name) 96 97 98def GetCareMap(which, imgname): 99 """Generates the care_map for the given partition.""" 100 assert which in PARTITIONS_WITH_CARE_MAP 101 102 simg = sparse_img.SparseImage(imgname) 103 care_map_ranges = simg.care_map 104 key = which + "_adjusted_partition_size" 105 adjusted_blocks = OPTIONS.info_dict.get(key) 106 if adjusted_blocks: 107 assert adjusted_blocks > 0, "blocks should be positive for " + which 108 care_map_ranges = care_map_ranges.intersect(rangelib.RangeSet( 109 "0-%d" % (adjusted_blocks,))) 110 111 return [which, care_map_ranges.to_string_raw()] 112 113 114def AddSystem(output_zip, prefix="IMAGES/", recovery_img=None, boot_img=None): 115 """Turn the contents of SYSTEM into a system image and store it in 116 output_zip. Returns the name of the system image file.""" 117 118 img = OutputFile(output_zip, OPTIONS.input_tmp, prefix, "system.img") 119 if os.path.exists(img.input_name): 120 print("system.img already exists in %s, no need to rebuild..." % (prefix,)) 121 return img.input_name 122 123 def output_sink(fn, data): 124 ofile = open(os.path.join(OPTIONS.input_tmp, "SYSTEM", fn), "w") 125 ofile.write(data) 126 ofile.close() 127 128 arc_name = "SYSTEM/" + fn 129 if arc_name in output_zip.namelist(): 130 OPTIONS.replace_updated_files_list.append(arc_name) 131 else: 132 common.ZipWrite(output_zip, ofile.name, arc_name) 133 134 if OPTIONS.rebuild_recovery: 135 print("Building new recovery patch") 136 common.MakeRecoveryPatch(OPTIONS.input_tmp, output_sink, recovery_img, 137 boot_img, info_dict=OPTIONS.info_dict) 138 139 block_list = OutputFile(output_zip, OPTIONS.input_tmp, prefix, "system.map") 140 CreateImage(OPTIONS.input_tmp, OPTIONS.info_dict, "system", img, 141 block_list=block_list) 142 143 return img.name 144 145 146def AddSystemOther(output_zip, prefix="IMAGES/"): 147 """Turn the contents of SYSTEM_OTHER into a system_other image 148 and store it in output_zip.""" 149 150 img = OutputFile(output_zip, OPTIONS.input_tmp, prefix, "system_other.img") 151 if os.path.exists(img.input_name): 152 print("system_other.img already exists in %s, no need to rebuild..." % ( 153 prefix,)) 154 return 155 156 CreateImage(OPTIONS.input_tmp, OPTIONS.info_dict, "system_other", img) 157 158 159def AddVendor(output_zip, prefix="IMAGES/"): 160 """Turn the contents of VENDOR into a vendor image and store in it 161 output_zip.""" 162 163 img = OutputFile(output_zip, OPTIONS.input_tmp, prefix, "vendor.img") 164 if os.path.exists(img.input_name): 165 print("vendor.img already exists in %s, no need to rebuild..." % (prefix,)) 166 return img.input_name 167 168 block_list = OutputFile(output_zip, OPTIONS.input_tmp, prefix, "vendor.map") 169 CreateImage(OPTIONS.input_tmp, OPTIONS.info_dict, "vendor", img, 170 block_list=block_list) 171 return img.name 172 173 174def AddProduct(output_zip, prefix="IMAGES/"): 175 """Turn the contents of PRODUCT into a product image and store it in output_zip.""" 176 177 img = OutputFile(output_zip, OPTIONS.input_tmp, prefix, "product.img") 178 if os.path.exists(img.input_name): 179 print("product.img already exists in %s, no need to rebuild..." % (prefix,)) 180 return img.input_name 181 182 block_list = OutputFile(output_zip, OPTIONS.input_tmp, prefix, "product.map") 183 CreateImage(OPTIONS.input_tmp, OPTIONS.info_dict, "product", img, 184 block_list=block_list) 185 return img.name 186 187 188def AddDtbo(output_zip, prefix="IMAGES/"): 189 """Adds the DTBO image. 190 191 Uses the image under prefix if it already exists. Otherwise looks for the 192 image under PREBUILT_IMAGES/, signs it as needed, and returns the image name. 193 """ 194 195 img = OutputFile(output_zip, OPTIONS.input_tmp, prefix, "dtbo.img") 196 if os.path.exists(img.input_name): 197 print("dtbo.img already exists in %s, no need to rebuild..." % (prefix,)) 198 return img.input_name 199 200 dtbo_prebuilt_path = os.path.join( 201 OPTIONS.input_tmp, "PREBUILT_IMAGES", "dtbo.img") 202 assert os.path.exists(dtbo_prebuilt_path) 203 shutil.copy(dtbo_prebuilt_path, img.name) 204 205 # AVB-sign the image as needed. 206 if OPTIONS.info_dict.get("avb_enable") == "true": 207 avbtool = os.getenv('AVBTOOL') or OPTIONS.info_dict["avb_avbtool"] 208 part_size = OPTIONS.info_dict["dtbo_size"] 209 # The AVB hash footer will be replaced if already present. 210 cmd = [avbtool, "add_hash_footer", "--image", img.name, 211 "--partition_size", str(part_size), "--partition_name", "dtbo"] 212 common.AppendAVBSigningArgs(cmd, "dtbo") 213 args = OPTIONS.info_dict.get("avb_dtbo_add_hash_footer_args") 214 if args and args.strip(): 215 cmd.extend(shlex.split(args)) 216 p = common.Run(cmd, stdout=subprocess.PIPE) 217 p.communicate() 218 assert p.returncode == 0, \ 219 "avbtool add_hash_footer of %s failed" % (img.name,) 220 221 img.Write() 222 return img.name 223 224 225def CreateImage(input_dir, info_dict, what, output_file, block_list=None): 226 print("creating " + what + ".img...") 227 228 image_props = build_image.ImagePropFromGlobalDict(info_dict, what) 229 fstab = info_dict["fstab"] 230 mount_point = "/" + what 231 if fstab and mount_point in fstab: 232 image_props["fs_type"] = fstab[mount_point].fs_type 233 234 # Use a fixed timestamp (01/01/2009) when packaging the image. 235 # Bug: 24377993 236 epoch = datetime.datetime.fromtimestamp(0) 237 timestamp = (datetime.datetime(2009, 1, 1) - epoch).total_seconds() 238 image_props["timestamp"] = int(timestamp) 239 240 if what == "system": 241 fs_config_prefix = "" 242 else: 243 fs_config_prefix = what + "_" 244 245 fs_config = os.path.join( 246 input_dir, "META/" + fs_config_prefix + "filesystem_config.txt") 247 if not os.path.exists(fs_config): 248 fs_config = None 249 250 # Override values loaded from info_dict. 251 if fs_config: 252 image_props["fs_config"] = fs_config 253 if block_list: 254 image_props["block_list"] = block_list.name 255 256 # Use repeatable ext4 FS UUID and hash_seed UUID (based on partition name and 257 # build fingerprint). 258 uuid_seed = what + "-" 259 if "build.prop" in info_dict: 260 build_prop = info_dict["build.prop"] 261 if "ro.build.fingerprint" in build_prop: 262 uuid_seed += build_prop["ro.build.fingerprint"] 263 elif "ro.build.thumbprint" in build_prop: 264 uuid_seed += build_prop["ro.build.thumbprint"] 265 image_props["uuid"] = str(uuid.uuid5(uuid.NAMESPACE_URL, uuid_seed)) 266 hash_seed = "hash_seed-" + uuid_seed 267 image_props["hash_seed"] = str(uuid.uuid5(uuid.NAMESPACE_URL, hash_seed)) 268 269 succ = build_image.BuildImage(os.path.join(input_dir, what.upper()), 270 image_props, output_file.name) 271 assert succ, "build " + what + ".img image failed" 272 273 output_file.Write() 274 if block_list: 275 block_list.Write() 276 277 # Set the 'adjusted_partition_size' that excludes the verity blocks of the 278 # given image. When avb is enabled, this size is the max image size returned 279 # by the avb tool. 280 is_verity_partition = "verity_block_device" in image_props 281 verity_supported = (image_props.get("verity") == "true" or 282 image_props.get("avb_enable") == "true") 283 is_avb_enable = image_props.get("avb_hashtree_enable") == "true" 284 if verity_supported and (is_verity_partition or is_avb_enable): 285 adjusted_blocks_value = image_props.get("partition_size") 286 if adjusted_blocks_value: 287 adjusted_blocks_key = what + "_adjusted_partition_size" 288 info_dict[adjusted_blocks_key] = int(adjusted_blocks_value)/4096 - 1 289 290 291def AddUserdata(output_zip, prefix="IMAGES/"): 292 """Create a userdata image and store it in output_zip. 293 294 In most case we just create and store an empty userdata.img; 295 But the invoker can also request to create userdata.img with real 296 data from the target files, by setting "userdata_img_with_data=true" 297 in OPTIONS.info_dict. 298 """ 299 300 img = OutputFile(output_zip, OPTIONS.input_tmp, prefix, "userdata.img") 301 if os.path.exists(img.input_name): 302 print("userdata.img already exists in %s, no need to rebuild..." % ( 303 prefix,)) 304 return 305 306 # Skip userdata.img if no size. 307 image_props = build_image.ImagePropFromGlobalDict(OPTIONS.info_dict, "data") 308 if not image_props.get("partition_size"): 309 return 310 311 print("creating userdata.img...") 312 313 # Use a fixed timestamp (01/01/2009) when packaging the image. 314 # Bug: 24377993 315 epoch = datetime.datetime.fromtimestamp(0) 316 timestamp = (datetime.datetime(2009, 1, 1) - epoch).total_seconds() 317 image_props["timestamp"] = int(timestamp) 318 319 if OPTIONS.info_dict.get("userdata_img_with_data") == "true": 320 user_dir = os.path.join(OPTIONS.input_tmp, "DATA") 321 else: 322 user_dir = common.MakeTempDir() 323 324 fstab = OPTIONS.info_dict["fstab"] 325 if fstab: 326 image_props["fs_type"] = fstab["/data"].fs_type 327 succ = build_image.BuildImage(user_dir, image_props, img.name) 328 assert succ, "build userdata.img image failed" 329 330 common.CheckSize(img.name, "userdata.img", OPTIONS.info_dict) 331 img.Write() 332 333 334def AppendVBMetaArgsForPartition(cmd, partition, img_path, public_key_dir): 335 if not img_path: 336 return 337 338 # Check if chain partition is used. 339 key_path = OPTIONS.info_dict.get("avb_" + partition + "_key_path") 340 if key_path: 341 # extract public key in AVB format to be included in vbmeta.img 342 avbtool = os.getenv('AVBTOOL') or OPTIONS.info_dict["avb_avbtool"] 343 public_key_path = os.path.join(public_key_dir, "%s.avbpubkey" % partition) 344 p = common.Run([avbtool, "extract_public_key", "--key", key_path, 345 "--output", public_key_path], 346 stdout=subprocess.PIPE, stderr=subprocess.PIPE) 347 p.communicate() 348 assert p.returncode == 0, \ 349 "avbtool extract_public_key fail for partition: %r" % partition 350 351 rollback_index_location = OPTIONS.info_dict[ 352 "avb_" + partition + "_rollback_index_location"] 353 cmd.extend(["--chain_partition", "%s:%s:%s" % ( 354 partition, rollback_index_location, public_key_path)]) 355 else: 356 cmd.extend(["--include_descriptors_from_image", img_path]) 357 358 359def AddVBMeta(output_zip, partitions, prefix="IMAGES/"): 360 """Creates a VBMeta image and store it in output_zip. 361 362 Args: 363 output_zip: The output zip file, which needs to be already open. 364 partitions: A dict that's keyed by partition names with image paths as 365 values. Only valid partition names are accepted, which include 'boot', 366 'recovery', 'system', 'vendor', 'dtbo'. 367 """ 368 img = OutputFile(output_zip, OPTIONS.input_tmp, prefix, "vbmeta.img") 369 if os.path.exists(img.input_name): 370 print("vbmeta.img already exists in %s; not rebuilding..." % (prefix,)) 371 return img.input_name 372 373 avbtool = os.getenv('AVBTOOL') or OPTIONS.info_dict["avb_avbtool"] 374 cmd = [avbtool, "make_vbmeta_image", "--output", img.name] 375 common.AppendAVBSigningArgs(cmd, "vbmeta") 376 377 public_key_dir = common.MakeTempDir(prefix="avbpubkey-") 378 for partition, path in partitions.items(): 379 assert partition in common.AVB_PARTITIONS, 'Unknown partition: %s' % ( 380 partition,) 381 assert os.path.exists(path), 'Failed to find %s for partition %s' % ( 382 path, partition) 383 AppendVBMetaArgsForPartition(cmd, partition, path, public_key_dir) 384 385 args = OPTIONS.info_dict.get("avb_vbmeta_args") 386 if args and args.strip(): 387 split_args = shlex.split(args) 388 for index, arg in enumerate(split_args[:-1]): 389 # Sanity check that the image file exists. Some images might be defined 390 # as a path relative to source tree, which may not be available at the 391 # same location when running this script (we have the input target_files 392 # zip only). For such cases, we additionally scan other locations (e.g. 393 # IMAGES/, RADIO/, etc) before bailing out. 394 if arg == '--include_descriptors_from_image': 395 image_path = split_args[index + 1] 396 if os.path.exists(image_path): 397 continue 398 found = False 399 for dir_name in ['IMAGES', 'RADIO', 'VENDOR_IMAGES', 'PREBUILT_IMAGES']: 400 alt_path = os.path.join( 401 OPTIONS.input_tmp, dir_name, os.path.basename(image_path)) 402 if os.path.exists(alt_path): 403 split_args[index + 1] = alt_path 404 found = True 405 break 406 assert found, 'failed to find %s' % (image_path,) 407 cmd.extend(split_args) 408 409 p = common.Run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 410 p.communicate() 411 assert p.returncode == 0, "avbtool make_vbmeta_image failed" 412 img.Write() 413 414 415def AddPartitionTable(output_zip, prefix="IMAGES/"): 416 """Create a partition table image and store it in output_zip.""" 417 418 img = OutputFile(output_zip, OPTIONS.input_tmp, prefix, "partition-table.img") 419 bpt = OutputFile(output_zip, OPTIONS.input_tmp, prefix, "partition-table.bpt") 420 421 # use BPTTOOL from environ, or "bpttool" if empty or not set. 422 bpttool = os.getenv("BPTTOOL") or "bpttool" 423 cmd = [bpttool, "make_table", "--output_json", bpt.name, 424 "--output_gpt", img.name] 425 input_files_str = OPTIONS.info_dict["board_bpt_input_files"] 426 input_files = input_files_str.split(" ") 427 for i in input_files: 428 cmd.extend(["--input", i]) 429 disk_size = OPTIONS.info_dict.get("board_bpt_disk_size") 430 if disk_size: 431 cmd.extend(["--disk_size", disk_size]) 432 args = OPTIONS.info_dict.get("board_bpt_make_table_args") 433 if args: 434 cmd.extend(shlex.split(args)) 435 436 p = common.Run(cmd, stdout=subprocess.PIPE) 437 p.communicate() 438 assert p.returncode == 0, "bpttool make_table failed" 439 440 img.Write() 441 bpt.Write() 442 443 444def AddCache(output_zip, prefix="IMAGES/"): 445 """Create an empty cache image and store it in output_zip.""" 446 447 img = OutputFile(output_zip, OPTIONS.input_tmp, prefix, "cache.img") 448 if os.path.exists(img.input_name): 449 print("cache.img already exists in %s, no need to rebuild..." % (prefix,)) 450 return 451 452 image_props = build_image.ImagePropFromGlobalDict(OPTIONS.info_dict, "cache") 453 # The build system has to explicitly request for cache.img. 454 if "fs_type" not in image_props: 455 return 456 457 print("creating cache.img...") 458 459 # Use a fixed timestamp (01/01/2009) when packaging the image. 460 # Bug: 24377993 461 epoch = datetime.datetime.fromtimestamp(0) 462 timestamp = (datetime.datetime(2009, 1, 1) - epoch).total_seconds() 463 image_props["timestamp"] = int(timestamp) 464 465 user_dir = common.MakeTempDir() 466 467 fstab = OPTIONS.info_dict["fstab"] 468 if fstab: 469 image_props["fs_type"] = fstab["/cache"].fs_type 470 succ = build_image.BuildImage(user_dir, image_props, img.name) 471 assert succ, "build cache.img image failed" 472 473 common.CheckSize(img.name, "cache.img", OPTIONS.info_dict) 474 img.Write() 475 476 477def AddRadioImagesForAbOta(output_zip, ab_partitions): 478 """Adds the radio images needed for A/B OTA to the output file. 479 480 It parses the list of A/B partitions, looks for the missing ones from RADIO/ 481 or VENDOR_IMAGES/ dirs, and copies them to IMAGES/ of the output file (or 482 dir). 483 484 It also ensures that on returning from the function all the listed A/B 485 partitions must have their images available under IMAGES/. 486 487 Args: 488 output_zip: The output zip file (needs to be already open), or None to 489 write images to OPTIONS.input_tmp/. 490 ab_partitions: The list of A/B partitions. 491 492 Raises: 493 AssertionError: If it can't find an image. 494 """ 495 for partition in ab_partitions: 496 img_name = partition.strip() + ".img" 497 prebuilt_path = os.path.join(OPTIONS.input_tmp, "IMAGES", img_name) 498 if os.path.exists(prebuilt_path): 499 print("%s already exists, no need to overwrite..." % (img_name,)) 500 continue 501 502 img_radio_path = os.path.join(OPTIONS.input_tmp, "RADIO", img_name) 503 if os.path.exists(img_radio_path): 504 if output_zip: 505 common.ZipWrite(output_zip, img_radio_path, "IMAGES/" + img_name) 506 else: 507 shutil.copy(img_radio_path, prebuilt_path) 508 continue 509 510 # Walk through VENDOR_IMAGES/ since files could be under subdirs. 511 img_vendor_dir = os.path.join(OPTIONS.input_tmp, "VENDOR_IMAGES") 512 for root, _, files in os.walk(img_vendor_dir): 513 if img_name in files: 514 if output_zip: 515 common.ZipWrite(output_zip, os.path.join(root, img_name), 516 "IMAGES/" + img_name) 517 else: 518 shutil.copy(os.path.join(root, img_name), prebuilt_path) 519 break 520 521 # Assert that the image is present under IMAGES/ now. 522 if output_zip: 523 # Zip spec says: All slashes MUST be forward slashes. 524 img_path = 'IMAGES/' + img_name 525 assert img_path in output_zip.namelist(), "cannot find " + img_name 526 else: 527 img_path = os.path.join(OPTIONS.input_tmp, "IMAGES", img_name) 528 assert os.path.exists(img_path), "cannot find " + img_name 529 530 531def AddCareMapTxtForAbOta(output_zip, ab_partitions, image_paths): 532 """Generates and adds care_map.txt for system and vendor partitions. 533 534 Args: 535 output_zip: The output zip file (needs to be already open), or None to 536 write images to OPTIONS.input_tmp/. 537 ab_partitions: The list of A/B partitions. 538 image_paths: A map from the partition name to the image path. 539 """ 540 care_map_list = [] 541 for partition in ab_partitions: 542 partition = partition.strip() 543 if partition not in PARTITIONS_WITH_CARE_MAP: 544 continue 545 546 verity_block_device = "{}_verity_block_device".format(partition) 547 avb_hashtree_enable = "avb_{}_hashtree_enable".format(partition) 548 if (verity_block_device in OPTIONS.info_dict or 549 OPTIONS.info_dict.get(avb_hashtree_enable) == "true"): 550 image_path = image_paths[partition] 551 assert os.path.exists(image_path) 552 care_map_list += GetCareMap(partition, image_path) 553 554 if care_map_list: 555 care_map_path = "META/care_map.txt" 556 if output_zip and care_map_path not in output_zip.namelist(): 557 common.ZipWriteStr(output_zip, care_map_path, '\n'.join(care_map_list)) 558 else: 559 with open(os.path.join(OPTIONS.input_tmp, care_map_path), 'w') as fp: 560 fp.write('\n'.join(care_map_list)) 561 if output_zip: 562 OPTIONS.replace_updated_files_list.append(care_map_path) 563 564 565def AddPackRadioImages(output_zip, images): 566 """Copies images listed in META/pack_radioimages.txt from RADIO/ to IMAGES/. 567 568 Args: 569 output_zip: The output zip file (needs to be already open), or None to 570 write images to OPTIONS.input_tmp/. 571 images: A list of image names. 572 573 Raises: 574 AssertionError: If a listed image can't be found. 575 """ 576 for image in images: 577 img_name = image.strip() 578 _, ext = os.path.splitext(img_name) 579 if not ext: 580 img_name += ".img" 581 582 prebuilt_path = os.path.join(OPTIONS.input_tmp, "IMAGES", img_name) 583 if os.path.exists(prebuilt_path): 584 print("%s already exists, no need to overwrite..." % (img_name,)) 585 continue 586 587 img_radio_path = os.path.join(OPTIONS.input_tmp, "RADIO", img_name) 588 assert os.path.exists(img_radio_path), \ 589 "Failed to find %s at %s" % (img_name, img_radio_path) 590 591 if output_zip: 592 common.ZipWrite(output_zip, img_radio_path, "IMAGES/" + img_name) 593 else: 594 shutil.copy(img_radio_path, prebuilt_path) 595 596 597def ReplaceUpdatedFiles(zip_filename, files_list): 598 """Updates all the ZIP entries listed in files_list. 599 600 For now the list includes META/care_map.txt, and the related files under 601 SYSTEM/ after rebuilding recovery. 602 """ 603 common.ZipDelete(zip_filename, files_list) 604 output_zip = zipfile.ZipFile(zip_filename, "a", 605 compression=zipfile.ZIP_DEFLATED, 606 allowZip64=True) 607 for item in files_list: 608 file_path = os.path.join(OPTIONS.input_tmp, item) 609 assert os.path.exists(file_path) 610 common.ZipWrite(output_zip, file_path, arcname=item) 611 common.ZipClose(output_zip) 612 613 614def AddImagesToTargetFiles(filename): 615 """Creates and adds images (boot/recovery/system/...) to a target_files.zip. 616 617 It works with either a zip file (zip mode), or a directory that contains the 618 files to be packed into a target_files.zip (dir mode). The latter is used when 619 being called from build/make/core/Makefile. 620 621 The images will be created under IMAGES/ in the input target_files.zip. 622 623 Args: 624 filename: the target_files.zip, or the zip root directory. 625 """ 626 if os.path.isdir(filename): 627 OPTIONS.input_tmp = os.path.abspath(filename) 628 input_zip = None 629 else: 630 OPTIONS.input_tmp, input_zip = common.UnzipTemp(filename) 631 632 if not OPTIONS.add_missing: 633 if os.path.isdir(os.path.join(OPTIONS.input_tmp, "IMAGES")): 634 print("target_files appears to already contain images.") 635 sys.exit(1) 636 637 # {vendor,product}.img is unlike system.img or system_other.img. Because it could 638 # be built from source, or dropped into target_files.zip as a prebuilt blob. 639 # We consider either of them as {vendor,product}.img being available, which could 640 # be used when generating vbmeta.img for AVB. 641 has_vendor = (os.path.isdir(os.path.join(OPTIONS.input_tmp, "VENDOR")) or 642 os.path.exists(os.path.join(OPTIONS.input_tmp, "IMAGES", 643 "vendor.img"))) 644 has_product = (os.path.isdir(os.path.join(OPTIONS.input_tmp, "PRODUCT")) or 645 os.path.exists(os.path.join(OPTIONS.input_tmp, "IMAGES", 646 "product.img"))) 647 has_system_other = os.path.isdir(os.path.join(OPTIONS.input_tmp, 648 "SYSTEM_OTHER")) 649 650 if input_zip: 651 OPTIONS.info_dict = common.LoadInfoDict(input_zip, OPTIONS.input_tmp) 652 653 common.ZipClose(input_zip) 654 output_zip = zipfile.ZipFile(filename, "a", 655 compression=zipfile.ZIP_DEFLATED, 656 allowZip64=True) 657 else: 658 OPTIONS.info_dict = common.LoadInfoDict(filename, filename) 659 output_zip = None 660 661 # Always make input_tmp/IMAGES available, since we may stage boot / recovery 662 # images there even under zip mode. The directory will be cleaned up as part 663 # of OPTIONS.input_tmp. 664 images_dir = os.path.join(OPTIONS.input_tmp, "IMAGES") 665 if not os.path.isdir(images_dir): 666 os.makedirs(images_dir) 667 668 has_recovery = (OPTIONS.info_dict.get("no_recovery") != "true") 669 670 # A map between partition names and their paths, which could be used when 671 # generating AVB vbmeta image. 672 partitions = dict() 673 674 def banner(s): 675 print("\n\n++++ " + s + " ++++\n\n") 676 677 banner("boot") 678 # common.GetBootableImage() returns the image directly if present. 679 boot_image = common.GetBootableImage( 680 "IMAGES/boot.img", "boot.img", OPTIONS.input_tmp, "BOOT") 681 # boot.img may be unavailable in some targets (e.g. aosp_arm64). 682 if boot_image: 683 partitions['boot'] = os.path.join(OPTIONS.input_tmp, "IMAGES", "boot.img") 684 if not os.path.exists(partitions['boot']): 685 boot_image.WriteToDir(OPTIONS.input_tmp) 686 if output_zip: 687 boot_image.AddToZip(output_zip) 688 689 recovery_image = None 690 if has_recovery: 691 banner("recovery") 692 recovery_image = common.GetBootableImage( 693 "IMAGES/recovery.img", "recovery.img", OPTIONS.input_tmp, "RECOVERY") 694 assert recovery_image, "Failed to create recovery.img." 695 partitions['recovery'] = os.path.join( 696 OPTIONS.input_tmp, "IMAGES", "recovery.img") 697 if not os.path.exists(partitions['recovery']): 698 recovery_image.WriteToDir(OPTIONS.input_tmp) 699 if output_zip: 700 recovery_image.AddToZip(output_zip) 701 702 banner("recovery (two-step image)") 703 # The special recovery.img for two-step package use. 704 recovery_two_step_image = common.GetBootableImage( 705 "IMAGES/recovery-two-step.img", "recovery-two-step.img", 706 OPTIONS.input_tmp, "RECOVERY", two_step_image=True) 707 assert recovery_two_step_image, "Failed to create recovery-two-step.img." 708 recovery_two_step_image_path = os.path.join( 709 OPTIONS.input_tmp, "IMAGES", "recovery-two-step.img") 710 if not os.path.exists(recovery_two_step_image_path): 711 recovery_two_step_image.WriteToDir(OPTIONS.input_tmp) 712 if output_zip: 713 recovery_two_step_image.AddToZip(output_zip) 714 715 banner("system") 716 partitions['system'] = AddSystem( 717 output_zip, recovery_img=recovery_image, boot_img=boot_image) 718 719 if has_vendor: 720 banner("vendor") 721 partitions['vendor'] = AddVendor(output_zip) 722 723 if has_product: 724 banner("product") 725 partitions['product'] = AddProduct(output_zip) 726 727 if has_system_other: 728 banner("system_other") 729 AddSystemOther(output_zip) 730 731 if not OPTIONS.is_signing: 732 banner("userdata") 733 AddUserdata(output_zip) 734 banner("cache") 735 AddCache(output_zip) 736 737 if OPTIONS.info_dict.get("board_bpt_enable") == "true": 738 banner("partition-table") 739 AddPartitionTable(output_zip) 740 741 if OPTIONS.info_dict.get("has_dtbo") == "true": 742 banner("dtbo") 743 partitions['dtbo'] = AddDtbo(output_zip) 744 745 if OPTIONS.info_dict.get("avb_enable") == "true": 746 banner("vbmeta") 747 AddVBMeta(output_zip, partitions) 748 749 banner("radio") 750 ab_partitions_txt = os.path.join(OPTIONS.input_tmp, "META", 751 "ab_partitions.txt") 752 if os.path.exists(ab_partitions_txt): 753 with open(ab_partitions_txt, 'r') as f: 754 ab_partitions = f.readlines() 755 756 # For devices using A/B update, copy over images from RADIO/ and/or 757 # VENDOR_IMAGES/ to IMAGES/ and make sure we have all the needed 758 # images ready under IMAGES/. All images should have '.img' as extension. 759 AddRadioImagesForAbOta(output_zip, ab_partitions) 760 761 # Generate care_map.txt for system and vendor partitions (if present), then 762 # write this file to target_files package. 763 AddCareMapTxtForAbOta(output_zip, ab_partitions, partitions) 764 765 # Radio images that need to be packed into IMAGES/, and product-img.zip. 766 pack_radioimages_txt = os.path.join( 767 OPTIONS.input_tmp, "META", "pack_radioimages.txt") 768 if os.path.exists(pack_radioimages_txt): 769 with open(pack_radioimages_txt, 'r') as f: 770 AddPackRadioImages(output_zip, f.readlines()) 771 772 if output_zip: 773 common.ZipClose(output_zip) 774 if OPTIONS.replace_updated_files_list: 775 ReplaceUpdatedFiles(output_zip.filename, 776 OPTIONS.replace_updated_files_list) 777 778 779def main(argv): 780 def option_handler(o, a): 781 if o in ("-a", "--add_missing"): 782 OPTIONS.add_missing = True 783 elif o in ("-r", "--rebuild_recovery",): 784 OPTIONS.rebuild_recovery = True 785 elif o == "--replace_verity_private_key": 786 OPTIONS.replace_verity_private_key = (True, a) 787 elif o == "--replace_verity_public_key": 788 OPTIONS.replace_verity_public_key = (True, a) 789 elif o == "--is_signing": 790 OPTIONS.is_signing = True 791 else: 792 return False 793 return True 794 795 args = common.ParseOptions( 796 argv, __doc__, extra_opts="ar", 797 extra_long_opts=["add_missing", "rebuild_recovery", 798 "replace_verity_public_key=", 799 "replace_verity_private_key=", 800 "is_signing"], 801 extra_option_handler=option_handler) 802 803 804 if len(args) != 1: 805 common.Usage(__doc__) 806 sys.exit(1) 807 808 AddImagesToTargetFiles(args[0]) 809 print("done.") 810 811if __name__ == '__main__': 812 try: 813 common.CloseInheritedPipes() 814 main(sys.argv[1:]) 815 except common.ExternalError as e: 816 print("\n ERROR: %s\n" % (e,)) 817 sys.exit(1) 818 finally: 819 common.Cleanup() 820