1 #!/usr/bin/env python 2 # 3 # Copyright (C) 2011 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 """ 18 Build image output_image_file from input_directory, properties_file, and target_out_dir 19 20 Usage: build_image input_directory properties_file output_image_file target_out_dir 21 22 """ 23 import os 24 import os.path 25 import re 26 import subprocess 27 import sys 28 import common 29 import shlex 30 import shutil 31 import sparse_img 32 import tempfile 33 34 OPTIONS = common.OPTIONS 35 36 FIXED_SALT = "aee087a5be3b982978c923f566a94613496b417f2af592639bc80d141e34dfe7" 37 BLOCK_SIZE = 4096 38 39 def RunCommand(cmd): 40 """Echo and run the given command. 41 42 Args: 43 cmd: the command represented as a list of strings. 44 Returns: 45 A tuple of the output and the exit code. 46 """ 47 print "Running: ", " ".join(cmd) 48 p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 49 output, _ = p.communicate() 50 print "%s" % (output.rstrip(),) 51 return (output, p.returncode) 52 53 def GetVerityFECSize(partition_size): 54 cmd = ["fec", "-s", str(partition_size)] 55 output, exit_code = RunCommand(cmd) 56 if exit_code != 0: 57 return False, 0 58 return True, int(output) 59 60 def GetVerityTreeSize(partition_size): 61 cmd = ["build_verity_tree", "-s", str(partition_size)] 62 output, exit_code = RunCommand(cmd) 63 if exit_code != 0: 64 return False, 0 65 return True, int(output) 66 67 def GetVerityMetadataSize(partition_size): 68 cmd = ["system/extras/verity/build_verity_metadata.py", "size", 69 str(partition_size)] 70 output, exit_code = RunCommand(cmd) 71 if exit_code != 0: 72 return False, 0 73 return True, int(output) 74 75 def GetVeritySize(partition_size, fec_supported): 76 success, verity_tree_size = GetVerityTreeSize(partition_size) 77 if not success: 78 return 0 79 success, verity_metadata_size = GetVerityMetadataSize(partition_size) 80 if not success: 81 return 0 82 verity_size = verity_tree_size + verity_metadata_size 83 if fec_supported: 84 success, fec_size = GetVerityFECSize(partition_size + verity_size) 85 if not success: 86 return 0 87 return verity_size + fec_size 88 return verity_size 89 90 def GetSimgSize(image_file): 91 simg = sparse_img.SparseImage(image_file, build_map=False) 92 return simg.blocksize * simg.total_blocks 93 94 def ZeroPadSimg(image_file, pad_size): 95 blocks = pad_size // BLOCK_SIZE 96 print("Padding %d blocks (%d bytes)" % (blocks, pad_size)) 97 simg = sparse_img.SparseImage(image_file, mode="r+b", build_map=False) 98 simg.AppendFillChunk(0, blocks) 99 100 def AVBCalcMaxImageSize(avbtool, partition_size, additional_args): 101 """Calculates max image size for a given partition size. 102 103 Args: 104 avbtool: String with path to avbtool. 105 partition_size: The size of the partition in question. 106 additional_args: Additional arguments to pass to 'avbtool 107 add_hashtree_image'. 108 Returns: 109 The maximum image size or 0 if an error occurred. 110 """ 111 cmdline = "%s add_hashtree_footer " % avbtool 112 cmdline += "--partition_size %d " % partition_size 113 cmdline += "--calc_max_image_size " 114 cmdline += additional_args 115 (output, exit_code) = RunCommand(shlex.split(cmdline)) 116 if exit_code != 0: 117 return 0 118 else: 119 return int(output) 120 121 def AVBAddHashtree(image_path, avbtool, partition_size, partition_name, 122 signing_args, additional_args): 123 """Adds dm-verity hashtree and AVB metadata to an image. 124 125 Args: 126 image_path: Path to image to modify. 127 avbtool: String with path to avbtool. 128 partition_size: The size of the partition in question. 129 partition_name: The name of the partition - will be embedded in metadata. 130 signing_args: Arguments for signing the image. 131 additional_args: Additional arguments to pass to 'avbtool 132 add_hashtree_image'. 133 Returns: 134 True if the operation succeeded. 135 """ 136 cmdline = "%s add_hashtree_footer " % avbtool 137 cmdline += "--partition_size %d " % partition_size 138 cmdline += "--partition_name %s " % partition_name 139 cmdline += "--image %s " % image_path 140 cmdline += signing_args + " " 141 cmdline += additional_args 142 (_, exit_code) = RunCommand(shlex.split(cmdline)) 143 return exit_code == 0 144 145 def AdjustPartitionSizeForVerity(partition_size, fec_supported): 146 """Modifies the provided partition size to account for the verity metadata. 147 148 This information is used to size the created image appropriately. 149 Args: 150 partition_size: the size of the partition to be verified. 151 Returns: 152 A tuple of the size of the partition adjusted for verity metadata, and 153 the size of verity metadata. 154 """ 155 key = "%d %d" % (partition_size, fec_supported) 156 if key in AdjustPartitionSizeForVerity.results: 157 return AdjustPartitionSizeForVerity.results[key] 158 159 hi = partition_size 160 if hi % BLOCK_SIZE != 0: 161 hi = (hi // BLOCK_SIZE) * BLOCK_SIZE 162 163 # verity tree and fec sizes depend on the partition size, which 164 # means this estimate is always going to be unnecessarily small 165 verity_size = GetVeritySize(hi, fec_supported) 166 lo = partition_size - verity_size 167 result = lo 168 169 # do a binary search for the optimal size 170 while lo < hi: 171 i = ((lo + hi) // (2 * BLOCK_SIZE)) * BLOCK_SIZE 172 v = GetVeritySize(i, fec_supported) 173 if i + v <= partition_size: 174 if result < i: 175 result = i 176 verity_size = v 177 lo = i + BLOCK_SIZE 178 else: 179 hi = i 180 181 AdjustPartitionSizeForVerity.results[key] = (result, verity_size) 182 return (result, verity_size) 183 184 AdjustPartitionSizeForVerity.results = {} 185 186 def BuildVerityFEC(sparse_image_path, verity_path, verity_fec_path, 187 padding_size): 188 cmd = ["fec", "-e", "-p", str(padding_size), sparse_image_path, 189 verity_path, verity_fec_path] 190 output, exit_code = RunCommand(cmd) 191 if exit_code != 0: 192 print "Could not build FEC data! Error: %s" % output 193 return False 194 return True 195 196 def BuildVerityTree(sparse_image_path, verity_image_path, prop_dict): 197 cmd = ["build_verity_tree", "-A", FIXED_SALT, sparse_image_path, 198 verity_image_path] 199 output, exit_code = RunCommand(cmd) 200 if exit_code != 0: 201 print "Could not build verity tree! Error: %s" % output 202 return False 203 root, salt = output.split() 204 prop_dict["verity_root_hash"] = root 205 prop_dict["verity_salt"] = salt 206 return True 207 208 def BuildVerityMetadata(image_size, verity_metadata_path, root_hash, salt, 209 block_device, signer_path, key, signer_args): 210 cmd = ["system/extras/verity/build_verity_metadata.py", "build", 211 str(image_size), verity_metadata_path, root_hash, salt, block_device, 212 signer_path, key] 213 if signer_args: 214 cmd.append("--signer_args=\"%s\"" % (' '.join(signer_args),)) 215 output, exit_code = RunCommand(cmd) 216 if exit_code != 0: 217 print "Could not build verity metadata! Error: %s" % output 218 return False 219 return True 220 221 def Append2Simg(sparse_image_path, unsparse_image_path, error_message): 222 """Appends the unsparse image to the given sparse image. 223 224 Args: 225 sparse_image_path: the path to the (sparse) image 226 unsparse_image_path: the path to the (unsparse) image 227 Returns: 228 True on success, False on failure. 229 """ 230 cmd = ["append2simg", sparse_image_path, unsparse_image_path] 231 output, exit_code = RunCommand(cmd) 232 if exit_code != 0: 233 print "%s: %s" % (error_message, output) 234 return False 235 return True 236 237 def Append(target, file_to_append, error_message): 238 print "appending %s to %s" % (file_to_append, target) 239 with open(target, "a") as out_file: 240 with open(file_to_append, "r") as input_file: 241 for line in input_file: 242 out_file.write(line) 243 return True 244 245 def BuildVerifiedImage(data_image_path, verity_image_path, 246 verity_metadata_path, verity_fec_path, 247 padding_size, fec_supported): 248 if not Append(verity_image_path, verity_metadata_path, 249 "Could not append verity metadata!"): 250 return False 251 252 if fec_supported: 253 # build FEC for the entire partition, including metadata 254 if not BuildVerityFEC(data_image_path, verity_image_path, 255 verity_fec_path, padding_size): 256 return False 257 258 if not Append(verity_image_path, verity_fec_path, "Could not append FEC!"): 259 return False 260 261 if not Append2Simg(data_image_path, verity_image_path, 262 "Could not append verity data!"): 263 return False 264 return True 265 266 def UnsparseImage(sparse_image_path, replace=True): 267 img_dir = os.path.dirname(sparse_image_path) 268 unsparse_image_path = "unsparse_" + os.path.basename(sparse_image_path) 269 unsparse_image_path = os.path.join(img_dir, unsparse_image_path) 270 if os.path.exists(unsparse_image_path): 271 if replace: 272 os.unlink(unsparse_image_path) 273 else: 274 return True, unsparse_image_path 275 inflate_command = ["simg2img", sparse_image_path, unsparse_image_path] 276 (_, exit_code) = RunCommand(inflate_command) 277 if exit_code != 0: 278 os.remove(unsparse_image_path) 279 return False, None 280 return True, unsparse_image_path 281 282 def MakeVerityEnabledImage(out_file, fec_supported, prop_dict): 283 """Creates an image that is verifiable using dm-verity. 284 285 Args: 286 out_file: the location to write the verifiable image at 287 prop_dict: a dictionary of properties required for image creation and 288 verification 289 Returns: 290 True on success, False otherwise. 291 """ 292 # get properties 293 image_size = int(prop_dict["partition_size"]) 294 block_dev = prop_dict["verity_block_device"] 295 signer_key = prop_dict["verity_key"] + ".pk8" 296 if OPTIONS.verity_signer_path is not None: 297 signer_path = OPTIONS.verity_signer_path 298 else: 299 signer_path = prop_dict["verity_signer_cmd"] 300 signer_args = OPTIONS.verity_signer_args 301 302 # make a tempdir 303 tempdir_name = tempfile.mkdtemp(suffix="_verity_images") 304 305 # get partial image paths 306 verity_image_path = os.path.join(tempdir_name, "verity.img") 307 verity_metadata_path = os.path.join(tempdir_name, "verity_metadata.img") 308 verity_fec_path = os.path.join(tempdir_name, "verity_fec.img") 309 310 # build the verity tree and get the root hash and salt 311 if not BuildVerityTree(out_file, verity_image_path, prop_dict): 312 shutil.rmtree(tempdir_name, ignore_errors=True) 313 return False 314 315 # build the metadata blocks 316 root_hash = prop_dict["verity_root_hash"] 317 salt = prop_dict["verity_salt"] 318 if not BuildVerityMetadata(image_size, verity_metadata_path, root_hash, salt, 319 block_dev, signer_path, signer_key, signer_args): 320 shutil.rmtree(tempdir_name, ignore_errors=True) 321 return False 322 323 # build the full verified image 324 target_size = int(prop_dict["original_partition_size"]) 325 verity_size = int(prop_dict["verity_size"]) 326 327 padding_size = target_size - image_size - verity_size 328 assert padding_size >= 0 329 330 if not BuildVerifiedImage(out_file, 331 verity_image_path, 332 verity_metadata_path, 333 verity_fec_path, 334 padding_size, 335 fec_supported): 336 shutil.rmtree(tempdir_name, ignore_errors=True) 337 return False 338 339 shutil.rmtree(tempdir_name, ignore_errors=True) 340 return True 341 342 def ConvertBlockMapToBaseFs(block_map_file): 343 fd, base_fs_file = tempfile.mkstemp(prefix="script_gen_", 344 suffix=".base_fs") 345 os.close(fd) 346 347 convert_command = ["blk_alloc_to_base_fs", block_map_file, base_fs_file] 348 (_, exit_code) = RunCommand(convert_command) 349 if exit_code != 0: 350 os.remove(base_fs_file) 351 return None 352 return base_fs_file 353 354 def BuildImage(in_dir, prop_dict, out_file, target_out=None): 355 """Build an image to out_file from in_dir with property prop_dict. 356 357 Args: 358 in_dir: path of input directory. 359 prop_dict: property dictionary. 360 out_file: path of the output image file. 361 target_out: path of the product out directory to read device specific FS config files. 362 363 Returns: 364 True iff the image is built successfully. 365 """ 366 # system_root_image=true: build a system.img that combines the contents of 367 # /system and the ramdisk, and can be mounted at the root of the file system. 368 origin_in = in_dir 369 fs_config = prop_dict.get("fs_config") 370 base_fs_file = None 371 if (prop_dict.get("system_root_image") == "true" 372 and prop_dict["mount_point"] == "system"): 373 in_dir = tempfile.mkdtemp() 374 # Change the mount point to "/" 375 prop_dict["mount_point"] = "/" 376 if fs_config: 377 # We need to merge the fs_config files of system and ramdisk. 378 fd, merged_fs_config = tempfile.mkstemp(prefix="root_fs_config", 379 suffix=".txt") 380 os.close(fd) 381 with open(merged_fs_config, "w") as fw: 382 if "ramdisk_fs_config" in prop_dict: 383 with open(prop_dict["ramdisk_fs_config"]) as fr: 384 fw.writelines(fr.readlines()) 385 with open(fs_config) as fr: 386 fw.writelines(fr.readlines()) 387 fs_config = merged_fs_config 388 389 build_command = [] 390 fs_type = prop_dict.get("fs_type", "") 391 run_fsck = False 392 393 fs_spans_partition = True 394 if fs_type.startswith("squash"): 395 fs_spans_partition = False 396 397 is_verity_partition = "verity_block_device" in prop_dict 398 verity_supported = prop_dict.get("verity") == "true" 399 verity_fec_supported = prop_dict.get("verity_fec") == "true" 400 401 # Adjust the partition size to make room for the hashes if this is to be 402 # verified. 403 if verity_supported and is_verity_partition: 404 partition_size = int(prop_dict.get("partition_size")) 405 (adjusted_size, verity_size) = AdjustPartitionSizeForVerity(partition_size, 406 verity_fec_supported) 407 if not adjusted_size: 408 return False 409 prop_dict["partition_size"] = str(adjusted_size) 410 prop_dict["original_partition_size"] = str(partition_size) 411 prop_dict["verity_size"] = str(verity_size) 412 413 # Adjust partition size for AVB. 414 if prop_dict.get("avb_enable") == "true": 415 avbtool = prop_dict.get("avb_avbtool") 416 partition_size = int(prop_dict.get("partition_size")) 417 additional_args = prop_dict["avb_add_hashtree_footer_args"] 418 max_image_size = AVBCalcMaxImageSize(avbtool, partition_size, 419 additional_args) 420 if max_image_size == 0: 421 return False 422 prop_dict["partition_size"] = str(max_image_size) 423 prop_dict["original_partition_size"] = str(partition_size) 424 425 if fs_type.startswith("ext"): 426 build_command = [prop_dict["ext_mkuserimg"]] 427 if "extfs_sparse_flag" in prop_dict: 428 build_command.append(prop_dict["extfs_sparse_flag"]) 429 run_fsck = True 430 build_command.extend([in_dir, out_file, fs_type, 431 prop_dict["mount_point"]]) 432 build_command.append(prop_dict["partition_size"]) 433 if "journal_size" in prop_dict: 434 build_command.extend(["-j", prop_dict["journal_size"]]) 435 if "timestamp" in prop_dict: 436 build_command.extend(["-T", str(prop_dict["timestamp"])]) 437 if fs_config: 438 build_command.extend(["-C", fs_config]) 439 if target_out: 440 build_command.extend(["-D", target_out]) 441 if "block_list" in prop_dict: 442 build_command.extend(["-B", prop_dict["block_list"]]) 443 if "base_fs_file" in prop_dict: 444 base_fs_file = ConvertBlockMapToBaseFs(prop_dict["base_fs_file"]) 445 if base_fs_file is None: 446 return False 447 build_command.extend(["-d", base_fs_file]) 448 build_command.extend(["-L", prop_dict["mount_point"]]) 449 if "extfs_inode_count" in prop_dict: 450 build_command.extend(["-i", prop_dict["extfs_inode_count"]]) 451 if "flash_erase_block_size" in prop_dict: 452 build_command.extend(["-e", prop_dict["flash_erase_block_size"]]) 453 if "flash_logical_block_size" in prop_dict: 454 build_command.extend(["-o", prop_dict["flash_logical_block_size"]]) 455 if "selinux_fc" in prop_dict: 456 build_command.append(prop_dict["selinux_fc"]) 457 elif fs_type.startswith("squash"): 458 build_command = ["mksquashfsimage.sh"] 459 build_command.extend([in_dir, out_file]) 460 if "squashfs_sparse_flag" in prop_dict: 461 build_command.extend([prop_dict["squashfs_sparse_flag"]]) 462 build_command.extend(["-m", prop_dict["mount_point"]]) 463 if target_out: 464 build_command.extend(["-d", target_out]) 465 if fs_config: 466 build_command.extend(["-C", fs_config]) 467 if "selinux_fc" in prop_dict: 468 build_command.extend(["-c", prop_dict["selinux_fc"]]) 469 if "block_list" in prop_dict: 470 build_command.extend(["-B", prop_dict["block_list"]]) 471 if "squashfs_compressor" in prop_dict: 472 build_command.extend(["-z", prop_dict["squashfs_compressor"]]) 473 if "squashfs_compressor_opt" in prop_dict: 474 build_command.extend(["-zo", prop_dict["squashfs_compressor_opt"]]) 475 if "squashfs_block_size" in prop_dict: 476 build_command.extend(["-b", prop_dict["squashfs_block_size"]]) 477 if "squashfs_disable_4k_align" in prop_dict and prop_dict.get("squashfs_disable_4k_align") == "true": 478 build_command.extend(["-a"]) 479 elif fs_type.startswith("f2fs"): 480 build_command = ["mkf2fsuserimg.sh"] 481 build_command.extend([out_file, prop_dict["partition_size"]]) 482 else: 483 print("Error: unknown filesystem type '%s'" % (fs_type)) 484 return False 485 486 if in_dir != origin_in: 487 # Construct a staging directory of the root file system. 488 ramdisk_dir = prop_dict.get("ramdisk_dir") 489 if ramdisk_dir: 490 shutil.rmtree(in_dir) 491 shutil.copytree(ramdisk_dir, in_dir, symlinks=True) 492 staging_system = os.path.join(in_dir, "system") 493 shutil.rmtree(staging_system, ignore_errors=True) 494 shutil.copytree(origin_in, staging_system, symlinks=True) 495 496 has_reserved_blocks = prop_dict.get("has_ext4_reserved_blocks") == "true" 497 ext4fs_output = None 498 499 try: 500 if fs_type.startswith("ext4"): 501 (ext4fs_output, exit_code) = RunCommand(build_command) 502 else: 503 (_, exit_code) = RunCommand(build_command) 504 finally: 505 if in_dir != origin_in: 506 # Clean up temporary directories and files. 507 shutil.rmtree(in_dir, ignore_errors=True) 508 if fs_config: 509 os.remove(fs_config) 510 if base_fs_file is not None: 511 os.remove(base_fs_file) 512 if exit_code != 0: 513 return False 514 515 # Bug: 21522719, 22023465 516 # There are some reserved blocks on ext4 FS (lesser of 4096 blocks and 2%). 517 # We need to deduct those blocks from the available space, since they are 518 # not writable even with root privilege. It only affects devices using 519 # file-based OTA and a kernel version of 3.10 or greater (currently just 520 # sprout). 521 # Separately, check if there's enough headroom space available. This is useful for 522 # devices with low disk space that have system image variation between builds. 523 if (has_reserved_blocks or "partition_headroom" in prop_dict) and fs_type.startswith("ext4"): 524 assert ext4fs_output is not None 525 ext4fs_stats = re.compile( 526 r'Created filesystem with .* (?P<used_blocks>[0-9]+)/' 527 r'(?P<total_blocks>[0-9]+) blocks') 528 m = ext4fs_stats.match(ext4fs_output.strip().split('\n')[-1]) 529 used_blocks = int(m.groupdict().get('used_blocks')) 530 total_blocks = int(m.groupdict().get('total_blocks')) 531 reserved_blocks = 0 532 headroom_blocks = 0 533 adjusted_blocks = total_blocks 534 if has_reserved_blocks: 535 reserved_blocks = min(4096, int(total_blocks * 0.02)) 536 adjusted_blocks -= reserved_blocks 537 if "partition_headroom" in prop_dict: 538 headroom_blocks = int(prop_dict.get('partition_headroom')) / BLOCK_SIZE 539 adjusted_blocks -= headroom_blocks 540 if used_blocks > adjusted_blocks: 541 mount_point = prop_dict.get("mount_point") 542 print("Error: Not enough room on %s (total: %d blocks, used: %d blocks, " 543 "reserved: %d blocks, headroom: %d blocks, available: %d blocks)" % ( 544 mount_point, total_blocks, used_blocks, reserved_blocks, 545 headroom_blocks, adjusted_blocks)) 546 return False 547 548 if not fs_spans_partition: 549 mount_point = prop_dict.get("mount_point") 550 partition_size = int(prop_dict.get("partition_size")) 551 image_size = GetSimgSize(out_file) 552 if image_size > partition_size: 553 print("Error: %s image size of %d is larger than partition size of " 554 "%d" % (mount_point, image_size, partition_size)) 555 return False 556 if verity_supported and is_verity_partition: 557 ZeroPadSimg(out_file, partition_size - image_size) 558 559 # create the verified image if this is to be verified 560 if verity_supported and is_verity_partition: 561 if not MakeVerityEnabledImage(out_file, verity_fec_supported, prop_dict): 562 return False 563 564 # Add AVB hashtree and metadata. 565 if "avb_enable" in prop_dict: 566 avbtool = prop_dict.get("avb_avbtool") 567 original_partition_size = int(prop_dict.get("original_partition_size")) 568 partition_name = prop_dict["partition_name"] 569 signing_args = prop_dict["avb_signing_args"] 570 additional_args = prop_dict["avb_add_hashtree_footer_args"] 571 if not AVBAddHashtree(out_file, avbtool, original_partition_size, 572 partition_name, signing_args, additional_args): 573 return False 574 575 if run_fsck and prop_dict.get("skip_fsck") != "true": 576 success, unsparse_image = UnsparseImage(out_file, replace=False) 577 if not success: 578 return False 579 580 # Run e2fsck on the inflated image file 581 e2fsck_command = ["e2fsck", "-f", "-n", unsparse_image] 582 (_, exit_code) = RunCommand(e2fsck_command) 583 584 os.remove(unsparse_image) 585 586 return exit_code == 0 587 588 589 def ImagePropFromGlobalDict(glob_dict, mount_point): 590 """Build an image property dictionary from the global dictionary. 591 592 Args: 593 glob_dict: the global dictionary from the build system. 594 mount_point: such as "system", "data" etc. 595 """ 596 d = {} 597 598 if "build.prop" in glob_dict: 599 bp = glob_dict["build.prop"] 600 if "ro.build.date.utc" in bp: 601 d["timestamp"] = bp["ro.build.date.utc"] 602 603 def copy_prop(src_p, dest_p): 604 if src_p in glob_dict: 605 d[dest_p] = str(glob_dict[src_p]) 606 607 common_props = ( 608 "extfs_sparse_flag", 609 "squashfs_sparse_flag", 610 "selinux_fc", 611 "skip_fsck", 612 "ext_mkuserimg", 613 "verity", 614 "verity_key", 615 "verity_signer_cmd", 616 "verity_fec", 617 "avb_signing_args", 618 "avb_avbtool" 619 ) 620 for p in common_props: 621 copy_prop(p, p) 622 623 d["mount_point"] = mount_point 624 if mount_point == "system": 625 copy_prop("fs_type", "fs_type") 626 # Copy the generic system fs type first, override with specific one if 627 # available. 628 copy_prop("system_fs_type", "fs_type") 629 copy_prop("system_headroom", "partition_headroom") 630 copy_prop("system_size", "partition_size") 631 copy_prop("system_journal_size", "journal_size") 632 copy_prop("system_verity_block_device", "verity_block_device") 633 copy_prop("system_root_image", "system_root_image") 634 copy_prop("ramdisk_dir", "ramdisk_dir") 635 copy_prop("ramdisk_fs_config", "ramdisk_fs_config") 636 copy_prop("has_ext4_reserved_blocks", "has_ext4_reserved_blocks") 637 copy_prop("system_squashfs_compressor", "squashfs_compressor") 638 copy_prop("system_squashfs_compressor_opt", "squashfs_compressor_opt") 639 copy_prop("system_squashfs_block_size", "squashfs_block_size") 640 copy_prop("system_squashfs_disable_4k_align", "squashfs_disable_4k_align") 641 copy_prop("system_base_fs_file", "base_fs_file") 642 copy_prop("system_avb_enable", "avb_enable") 643 copy_prop("system_avb_add_hashtree_footer_args", 644 "avb_add_hashtree_footer_args") 645 copy_prop("system_extfs_inode_count", "extfs_inode_count") 646 elif mount_point == "system_other": 647 # We inherit the selinux policies of /system since we contain some of its files. 648 d["mount_point"] = "system" 649 copy_prop("fs_type", "fs_type") 650 copy_prop("system_fs_type", "fs_type") 651 copy_prop("system_size", "partition_size") 652 copy_prop("system_journal_size", "journal_size") 653 copy_prop("system_verity_block_device", "verity_block_device") 654 copy_prop("has_ext4_reserved_blocks", "has_ext4_reserved_blocks") 655 copy_prop("system_squashfs_compressor", "squashfs_compressor") 656 copy_prop("system_squashfs_compressor_opt", "squashfs_compressor_opt") 657 copy_prop("system_squashfs_block_size", "squashfs_block_size") 658 copy_prop("system_base_fs_file", "base_fs_file") 659 copy_prop("system_avb_enable", "avb_enable") 660 copy_prop("system_avb_add_hashtree_footer_args", 661 "avb_add_hashtree_footer_args") 662 copy_prop("system_extfs_inode_count", "extfs_inode_count") 663 elif mount_point == "data": 664 # Copy the generic fs type first, override with specific one if available. 665 copy_prop("fs_type", "fs_type") 666 copy_prop("userdata_fs_type", "fs_type") 667 copy_prop("userdata_size", "partition_size") 668 copy_prop("flash_logical_block_size","flash_logical_block_size") 669 copy_prop("flash_erase_block_size", "flash_erase_block_size") 670 elif mount_point == "cache": 671 copy_prop("cache_fs_type", "fs_type") 672 copy_prop("cache_size", "partition_size") 673 elif mount_point == "vendor": 674 copy_prop("vendor_fs_type", "fs_type") 675 copy_prop("vendor_size", "partition_size") 676 copy_prop("vendor_journal_size", "journal_size") 677 copy_prop("vendor_verity_block_device", "verity_block_device") 678 copy_prop("has_ext4_reserved_blocks", "has_ext4_reserved_blocks") 679 copy_prop("vendor_squashfs_compressor", "squashfs_compressor") 680 copy_prop("vendor_squashfs_compressor_opt", "squashfs_compressor_opt") 681 copy_prop("vendor_squashfs_block_size", "squashfs_block_size") 682 copy_prop("vendor_squashfs_disable_4k_align", "squashfs_disable_4k_align") 683 copy_prop("vendor_base_fs_file", "base_fs_file") 684 copy_prop("vendor_avb_enable", "avb_enable") 685 copy_prop("vendor_avb_add_hashtree_footer_args", 686 "avb_add_hashtree_footer_args") 687 copy_prop("vendor_extfs_inode_count", "extfs_inode_count") 688 elif mount_point == "oem": 689 copy_prop("fs_type", "fs_type") 690 copy_prop("oem_size", "partition_size") 691 copy_prop("oem_journal_size", "journal_size") 692 copy_prop("has_ext4_reserved_blocks", "has_ext4_reserved_blocks") 693 copy_prop("oem_extfs_inode_count", "extfs_inode_count") 694 d["partition_name"] = mount_point 695 return d 696 697 698 def LoadGlobalDict(filename): 699 """Load "name=value" pairs from filename""" 700 d = {} 701 f = open(filename) 702 for line in f: 703 line = line.strip() 704 if not line or line.startswith("#"): 705 continue 706 k, v = line.split("=", 1) 707 d[k] = v 708 f.close() 709 return d 710 711 712 def main(argv): 713 if len(argv) != 4: 714 print __doc__ 715 sys.exit(1) 716 717 in_dir = argv[0] 718 glob_dict_file = argv[1] 719 out_file = argv[2] 720 target_out = argv[3] 721 722 glob_dict = LoadGlobalDict(glob_dict_file) 723 if "mount_point" in glob_dict: 724 # The caller knows the mount point and provides a dictionay needed by 725 # BuildImage(). 726 image_properties = glob_dict 727 else: 728 image_filename = os.path.basename(out_file) 729 mount_point = "" 730 if image_filename == "system.img": 731 mount_point = "system" 732 elif image_filename == "system_other.img": 733 mount_point = "system_other" 734 elif image_filename == "userdata.img": 735 mount_point = "data" 736 elif image_filename == "cache.img": 737 mount_point = "cache" 738 elif image_filename == "vendor.img": 739 mount_point = "vendor" 740 elif image_filename == "oem.img": 741 mount_point = "oem" 742 else: 743 print >> sys.stderr, "error: unknown image file name ", image_filename 744 exit(1) 745 746 image_properties = ImagePropFromGlobalDict(glob_dict, mount_point) 747 748 if not BuildImage(in_dir, image_properties, out_file, target_out): 749 print >> sys.stderr, "error: failed to build %s from %s" % (out_file, 750 in_dir) 751 exit(1) 752 753 754 if __name__ == '__main__': 755 main(sys.argv[1:]) 756