Home | History | Annotate | Download | only in releasetools
      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 Builds output_image from the given input_directory, properties_file,
     19 and writes the image to target_output_directory.
     20 
     21 Usage:  build_image.py input_directory properties_file output_image \\
     22             target_output_directory
     23 """
     24 
     25 from __future__ import print_function
     26 
     27 import logging
     28 import os
     29 import os.path
     30 import re
     31 import shutil
     32 import sys
     33 
     34 import common
     35 import verity_utils
     36 
     37 logger = logging.getLogger(__name__)
     38 
     39 OPTIONS = common.OPTIONS
     40 BLOCK_SIZE = common.BLOCK_SIZE
     41 BYTES_IN_MB = 1024 * 1024
     42 
     43 
     44 class BuildImageError(Exception):
     45   """An Exception raised during image building."""
     46 
     47   def __init__(self, message):
     48     Exception.__init__(self, message)
     49 
     50 
     51 def GetDiskUsage(path):
     52   """Returns the number of bytes that "path" occupies on host.
     53 
     54   Args:
     55     path: The directory or file to calculate size on.
     56 
     57   Returns:
     58     The number of bytes based on a 1K block_size.
     59   """
     60   cmd = ["du", "-k", "-s", path]
     61   output = common.RunAndCheckOutput(cmd, verbose=False)
     62   return int(output.split()[0]) * 1024
     63 
     64 
     65 def GetInodeUsage(path):
     66   """Returns the number of inodes that "path" occupies on host.
     67 
     68   Args:
     69     path: The directory or file to calculate inode number on.
     70 
     71   Returns:
     72     The number of inodes used.
     73   """
     74   cmd = ["find", path, "-print"]
     75   output = common.RunAndCheckOutput(cmd, verbose=False)
     76   # increase by > 4% as number of files and directories is not whole picture.
     77   inodes = output.count('\n')
     78   spare_inodes = inodes * 4 // 100
     79   min_spare_inodes = 12
     80   if spare_inodes < min_spare_inodes:
     81     spare_inodes = min_spare_inodes
     82   return inodes + spare_inodes
     83 
     84 
     85 def GetFilesystemCharacteristics(image_path, sparse_image=True):
     86   """Returns various filesystem characteristics of "image_path".
     87 
     88   Args:
     89     image_path: The file to analyze.
     90     sparse_image: Image is sparse
     91 
     92   Returns:
     93     The characteristics dictionary.
     94   """
     95   unsparse_image_path = image_path
     96   if sparse_image:
     97     unsparse_image_path = UnsparseImage(image_path, replace=False)
     98 
     99   cmd = ["tune2fs", "-l", unsparse_image_path]
    100   try:
    101     output = common.RunAndCheckOutput(cmd, verbose=False)
    102   finally:
    103     if sparse_image:
    104       os.remove(unsparse_image_path)
    105   fs_dict = {}
    106   for line in output.splitlines():
    107     fields = line.split(":")
    108     if len(fields) == 2:
    109       fs_dict[fields[0].strip()] = fields[1].strip()
    110   return fs_dict
    111 
    112 
    113 def UnsparseImage(sparse_image_path, replace=True):
    114   img_dir = os.path.dirname(sparse_image_path)
    115   unsparse_image_path = "unsparse_" + os.path.basename(sparse_image_path)
    116   unsparse_image_path = os.path.join(img_dir, unsparse_image_path)
    117   if os.path.exists(unsparse_image_path):
    118     if replace:
    119       os.unlink(unsparse_image_path)
    120     else:
    121       return unsparse_image_path
    122   inflate_command = ["simg2img", sparse_image_path, unsparse_image_path]
    123   try:
    124     common.RunAndCheckOutput(inflate_command)
    125   except:
    126     os.remove(unsparse_image_path)
    127     raise
    128   return unsparse_image_path
    129 
    130 
    131 def ConvertBlockMapToBaseFs(block_map_file):
    132   base_fs_file = common.MakeTempFile(prefix="script_gen_", suffix=".base_fs")
    133   convert_command = ["blk_alloc_to_base_fs", block_map_file, base_fs_file]
    134   common.RunAndCheckOutput(convert_command)
    135   return base_fs_file
    136 
    137 
    138 def SetUpInDirAndFsConfig(origin_in, prop_dict):
    139   """Returns the in_dir and fs_config that should be used for image building.
    140 
    141   When building system.img for all targets, it creates and returns a staged dir
    142   that combines the contents of /system (i.e. in the given in_dir) and root.
    143 
    144   Args:
    145     origin_in: Path to the input directory.
    146     prop_dict: A property dict that contains info like partition size. Values
    147         may be updated.
    148 
    149   Returns:
    150     A tuple of in_dir and fs_config that should be used to build the image.
    151   """
    152   fs_config = prop_dict.get("fs_config")
    153 
    154   if prop_dict["mount_point"] == "system_other":
    155     prop_dict["mount_point"] = "system"
    156     return origin_in, fs_config
    157 
    158   if prop_dict["mount_point"] != "system":
    159     return origin_in, fs_config
    160 
    161   if "first_pass" in prop_dict:
    162     prop_dict["mount_point"] = "/"
    163     return prop_dict["first_pass"]
    164 
    165   # Construct a staging directory of the root file system.
    166   in_dir = common.MakeTempDir()
    167   root_dir = prop_dict.get("root_dir")
    168   if root_dir:
    169     shutil.rmtree(in_dir)
    170     shutil.copytree(root_dir, in_dir, symlinks=True)
    171   in_dir_system = os.path.join(in_dir, "system")
    172   shutil.rmtree(in_dir_system, ignore_errors=True)
    173   shutil.copytree(origin_in, in_dir_system, symlinks=True)
    174 
    175   # Change the mount point to "/".
    176   prop_dict["mount_point"] = "/"
    177   if fs_config:
    178     # We need to merge the fs_config files of system and root.
    179     merged_fs_config = common.MakeTempFile(
    180         prefix="merged_fs_config", suffix=".txt")
    181     with open(merged_fs_config, "w") as fw:
    182       if "root_fs_config" in prop_dict:
    183         with open(prop_dict["root_fs_config"]) as fr:
    184           fw.writelines(fr.readlines())
    185       with open(fs_config) as fr:
    186         fw.writelines(fr.readlines())
    187     fs_config = merged_fs_config
    188   prop_dict["first_pass"] = (in_dir, fs_config)
    189   return in_dir, fs_config
    190 
    191 
    192 def CheckHeadroom(ext4fs_output, prop_dict):
    193   """Checks if there's enough headroom space available.
    194 
    195   Headroom is the reserved space on system image (via PRODUCT_SYSTEM_HEADROOM),
    196   which is useful for devices with low disk space that have system image
    197   variation between builds. The 'partition_headroom' in prop_dict is the size
    198   in bytes, while the numbers in 'ext4fs_output' are for 4K-blocks.
    199 
    200   Args:
    201     ext4fs_output: The output string from mke2fs command.
    202     prop_dict: The property dict.
    203 
    204   Raises:
    205     AssertionError: On invalid input.
    206     BuildImageError: On check failure.
    207   """
    208   assert ext4fs_output is not None
    209   assert prop_dict.get('fs_type', '').startswith('ext4')
    210   assert 'partition_headroom' in prop_dict
    211   assert 'mount_point' in prop_dict
    212 
    213   ext4fs_stats = re.compile(
    214       r'Created filesystem with .* (?P<used_blocks>[0-9]+)/'
    215       r'(?P<total_blocks>[0-9]+) blocks')
    216   last_line = ext4fs_output.strip().split('\n')[-1]
    217   m = ext4fs_stats.match(last_line)
    218   used_blocks = int(m.groupdict().get('used_blocks'))
    219   total_blocks = int(m.groupdict().get('total_blocks'))
    220   headroom_blocks = int(prop_dict['partition_headroom']) // BLOCK_SIZE
    221   adjusted_blocks = total_blocks - headroom_blocks
    222   if used_blocks > adjusted_blocks:
    223     mount_point = prop_dict["mount_point"]
    224     raise BuildImageError(
    225         "Error: Not enough room on {} (total: {} blocks, used: {} blocks, "
    226         "headroom: {} blocks, available: {} blocks)".format(
    227             mount_point, total_blocks, used_blocks, headroom_blocks,
    228             adjusted_blocks))
    229 
    230 
    231 def BuildImageMkfs(in_dir, prop_dict, out_file, target_out, fs_config):
    232   """Builds a pure image for the files under in_dir and writes it to out_file.
    233 
    234   Args:
    235     in_dir: Path to input directory.
    236     prop_dict: A property dict that contains info like partition size. Values
    237         will be updated with computed values.
    238     out_file: The output image file.
    239     target_out: Path to the TARGET_OUT directory as in Makefile. It actually
    240         points to the /system directory under PRODUCT_OUT. fs_config (the one
    241         under system/core/libcutils) reads device specific FS config files from
    242         there.
    243     fs_config: The fs_config file that drives the prototype
    244 
    245   Raises:
    246     BuildImageError: On build image failures.
    247   """
    248   build_command = []
    249   fs_type = prop_dict.get("fs_type", "")
    250   run_e2fsck = False
    251 
    252   if fs_type.startswith("ext"):
    253     build_command = [prop_dict["ext_mkuserimg"]]
    254     if "extfs_sparse_flag" in prop_dict:
    255       build_command.append(prop_dict["extfs_sparse_flag"])
    256       run_e2fsck = True
    257     build_command.extend([in_dir, out_file, fs_type,
    258                           prop_dict["mount_point"]])
    259     build_command.append(prop_dict["image_size"])
    260     if "journal_size" in prop_dict:
    261       build_command.extend(["-j", prop_dict["journal_size"]])
    262     if "timestamp" in prop_dict:
    263       build_command.extend(["-T", str(prop_dict["timestamp"])])
    264     if fs_config:
    265       build_command.extend(["-C", fs_config])
    266     if target_out:
    267       build_command.extend(["-D", target_out])
    268     if "block_list" in prop_dict:
    269       build_command.extend(["-B", prop_dict["block_list"]])
    270     if "base_fs_file" in prop_dict:
    271       base_fs_file = ConvertBlockMapToBaseFs(prop_dict["base_fs_file"])
    272       build_command.extend(["-d", base_fs_file])
    273     build_command.extend(["-L", prop_dict["mount_point"]])
    274     if "extfs_inode_count" in prop_dict:
    275       build_command.extend(["-i", prop_dict["extfs_inode_count"]])
    276     if "extfs_rsv_pct" in prop_dict:
    277       build_command.extend(["-M", prop_dict["extfs_rsv_pct"]])
    278     if "flash_erase_block_size" in prop_dict:
    279       build_command.extend(["-e", prop_dict["flash_erase_block_size"]])
    280     if "flash_logical_block_size" in prop_dict:
    281       build_command.extend(["-o", prop_dict["flash_logical_block_size"]])
    282     # Specify UUID and hash_seed if using mke2fs.
    283     if prop_dict["ext_mkuserimg"] == "mkuserimg_mke2fs":
    284       if "uuid" in prop_dict:
    285         build_command.extend(["-U", prop_dict["uuid"]])
    286       if "hash_seed" in prop_dict:
    287         build_command.extend(["-S", prop_dict["hash_seed"]])
    288     if "ext4_share_dup_blocks" in prop_dict:
    289       build_command.append("-c")
    290     build_command.extend(["--inode_size", "256"])
    291     if "selinux_fc" in prop_dict:
    292       build_command.append(prop_dict["selinux_fc"])
    293   elif fs_type.startswith("squash"):
    294     build_command = ["mksquashfsimage.sh"]
    295     build_command.extend([in_dir, out_file])
    296     if "squashfs_sparse_flag" in prop_dict:
    297       build_command.extend([prop_dict["squashfs_sparse_flag"]])
    298     build_command.extend(["-m", prop_dict["mount_point"]])
    299     if target_out:
    300       build_command.extend(["-d", target_out])
    301     if fs_config:
    302       build_command.extend(["-C", fs_config])
    303     if "selinux_fc" in prop_dict:
    304       build_command.extend(["-c", prop_dict["selinux_fc"]])
    305     if "block_list" in prop_dict:
    306       build_command.extend(["-B", prop_dict["block_list"]])
    307     if "squashfs_block_size" in prop_dict:
    308       build_command.extend(["-b", prop_dict["squashfs_block_size"]])
    309     if "squashfs_compressor" in prop_dict:
    310       build_command.extend(["-z", prop_dict["squashfs_compressor"]])
    311     if "squashfs_compressor_opt" in prop_dict:
    312       build_command.extend(["-zo", prop_dict["squashfs_compressor_opt"]])
    313     if prop_dict.get("squashfs_disable_4k_align") == "true":
    314       build_command.extend(["-a"])
    315   elif fs_type.startswith("f2fs"):
    316     build_command = ["mkf2fsuserimg.sh"]
    317     build_command.extend([out_file, prop_dict["image_size"]])
    318     if fs_config:
    319       build_command.extend(["-C", fs_config])
    320     build_command.extend(["-f", in_dir])
    321     if target_out:
    322       build_command.extend(["-D", target_out])
    323     if "selinux_fc" in prop_dict:
    324       build_command.extend(["-s", prop_dict["selinux_fc"]])
    325     build_command.extend(["-t", prop_dict["mount_point"]])
    326     if "timestamp" in prop_dict:
    327       build_command.extend(["-T", str(prop_dict["timestamp"])])
    328     build_command.extend(["-L", prop_dict["mount_point"]])
    329   else:
    330     raise BuildImageError(
    331         "Error: unknown filesystem type: {}".format(fs_type))
    332 
    333   try:
    334     mkfs_output = common.RunAndCheckOutput(build_command)
    335   except:
    336     try:
    337       du = GetDiskUsage(in_dir)
    338       du_str = "{} bytes ({} MB)".format(du, du // BYTES_IN_MB)
    339     # Suppress any errors from GetDiskUsage() to avoid hiding the real errors
    340     # from common.RunAndCheckOutput().
    341     except Exception:  # pylint: disable=broad-except
    342       logger.exception("Failed to compute disk usage with du")
    343       du_str = "unknown"
    344     print(
    345         "Out of space? Out of inodes? The tree size of {} is {}, "
    346         "with reserved space of {} bytes ({} MB).".format(
    347             in_dir, du_str,
    348             int(prop_dict.get("partition_reserved_size", 0)),
    349             int(prop_dict.get("partition_reserved_size", 0)) // BYTES_IN_MB))
    350     print(
    351         "The max image size for filesystem files is {} bytes ({} MB), out of a "
    352         "total partition size of {} bytes ({} MB).".format(
    353             int(prop_dict["image_size"]),
    354             int(prop_dict["image_size"]) // BYTES_IN_MB,
    355             int(prop_dict["partition_size"]),
    356             int(prop_dict["partition_size"]) // BYTES_IN_MB))
    357     raise
    358 
    359   if run_e2fsck and prop_dict.get("skip_fsck") != "true":
    360     unsparse_image = UnsparseImage(out_file, replace=False)
    361 
    362     # Run e2fsck on the inflated image file
    363     e2fsck_command = ["e2fsck", "-f", "-n", unsparse_image]
    364     try:
    365       common.RunAndCheckOutput(e2fsck_command)
    366     finally:
    367       os.remove(unsparse_image)
    368 
    369   return mkfs_output
    370 
    371 
    372 def BuildImage(in_dir, prop_dict, out_file, target_out=None):
    373   """Builds an image for the files under in_dir and writes it to out_file.
    374 
    375   Args:
    376     in_dir: Path to input directory.
    377     prop_dict: A property dict that contains info like partition size. Values
    378         will be updated with computed values.
    379     out_file: The output image file.
    380     target_out: Path to the TARGET_OUT directory as in Makefile. It actually
    381         points to the /system directory under PRODUCT_OUT. fs_config (the one
    382         under system/core/libcutils) reads device specific FS config files from
    383         there.
    384 
    385   Raises:
    386     BuildImageError: On build image failures.
    387   """
    388   in_dir, fs_config = SetUpInDirAndFsConfig(in_dir, prop_dict)
    389 
    390   build_command = []
    391   fs_type = prop_dict.get("fs_type", "")
    392 
    393   fs_spans_partition = True
    394   if fs_type.startswith("squash"):
    395     fs_spans_partition = False
    396 
    397   # Get a builder for creating an image that's to be verified by Verified Boot,
    398   # or None if not applicable.
    399   verity_image_builder = verity_utils.CreateVerityImageBuilder(prop_dict)
    400 
    401   if (prop_dict.get("use_dynamic_partition_size") == "true" and
    402       "partition_size" not in prop_dict):
    403     # If partition_size is not defined, use output of `du' + reserved_size.
    404     size = GetDiskUsage(in_dir)
    405     logger.info(
    406         "The tree size of %s is %d MB.", in_dir, size // BYTES_IN_MB)
    407     # If not specified, give us 16MB margin for GetDiskUsage error ...
    408     reserved_size = int(prop_dict.get("partition_reserved_size", BYTES_IN_MB * 16))
    409     partition_headroom = int(prop_dict.get("partition_headroom", 0))
    410     if fs_type.startswith("ext4") and partition_headroom > reserved_size:
    411       reserved_size = partition_headroom
    412     size += reserved_size
    413     # Round this up to a multiple of 4K so that avbtool works
    414     size = common.RoundUpTo4K(size)
    415     if fs_type.startswith("ext"):
    416       prop_dict["partition_size"] = str(size)
    417       prop_dict["image_size"] = str(size)
    418       if "extfs_inode_count" not in prop_dict:
    419         prop_dict["extfs_inode_count"] = str(GetInodeUsage(in_dir))
    420       logger.info(
    421           "First Pass based on estimates of %d MB and %s inodes.",
    422           size // BYTES_IN_MB, prop_dict["extfs_inode_count"])
    423       BuildImageMkfs(in_dir, prop_dict, out_file, target_out, fs_config)
    424       sparse_image = False
    425       if "extfs_sparse_flag" in prop_dict:
    426         sparse_image = True
    427       fs_dict = GetFilesystemCharacteristics(out_file, sparse_image)
    428       os.remove(out_file)
    429       block_size = int(fs_dict.get("Block size", "4096"))
    430       free_size = int(fs_dict.get("Free blocks", "0")) * block_size
    431       reserved_size = int(prop_dict.get("partition_reserved_size", 0))
    432       partition_headroom = int(fs_dict.get("partition_headroom", 0))
    433       if fs_type.startswith("ext4") and partition_headroom > reserved_size:
    434         reserved_size = partition_headroom
    435       if free_size <= reserved_size:
    436         logger.info(
    437             "Not worth reducing image %d <= %d.", free_size, reserved_size)
    438       else:
    439         size -= free_size
    440         size += reserved_size
    441         if reserved_size == 0:
    442           # add .3% margin
    443           size = size * 1003 // 1000
    444         # Use a minimum size, otherwise we will fail to calculate an AVB footer
    445         # or fail to construct an ext4 image.
    446         size = max(size, 256 * 1024)
    447         if block_size <= 4096:
    448           size = common.RoundUpTo4K(size)
    449         else:
    450           size = ((size + block_size - 1) // block_size) * block_size
    451       extfs_inode_count = prop_dict["extfs_inode_count"]
    452       inodes = int(fs_dict.get("Inode count", extfs_inode_count))
    453       inodes -= int(fs_dict.get("Free inodes", "0"))
    454       # add .2% margin or 1 inode, whichever is greater
    455       spare_inodes = inodes * 2 // 1000
    456       min_spare_inodes = 1
    457       if spare_inodes < min_spare_inodes:
    458         spare_inodes = min_spare_inodes
    459       inodes += spare_inodes
    460       prop_dict["extfs_inode_count"] = str(inodes)
    461       prop_dict["partition_size"] = str(size)
    462       logger.info(
    463           "Allocating %d Inodes for %s.", inodes, out_file)
    464     if verity_image_builder:
    465       size = verity_image_builder.CalculateDynamicPartitionSize(size)
    466     prop_dict["partition_size"] = str(size)
    467     logger.info(
    468         "Allocating %d MB for %s.", size // BYTES_IN_MB, out_file)
    469 
    470   prop_dict["image_size"] = prop_dict["partition_size"]
    471 
    472   # Adjust the image size to make room for the hashes if this is to be verified.
    473   if verity_image_builder:
    474     max_image_size = verity_image_builder.CalculateMaxImageSize()
    475     prop_dict["image_size"] = str(max_image_size)
    476 
    477   mkfs_output = BuildImageMkfs(in_dir, prop_dict, out_file, target_out, fs_config)
    478 
    479   # Check if there's enough headroom space available for ext4 image.
    480   if "partition_headroom" in prop_dict and fs_type.startswith("ext4"):
    481     CheckHeadroom(mkfs_output, prop_dict)
    482 
    483   if not fs_spans_partition and verity_image_builder:
    484     verity_image_builder.PadSparseImage(out_file)
    485 
    486   # Create the verified image if this is to be verified.
    487   if verity_image_builder:
    488     verity_image_builder.Build(out_file)
    489 
    490 
    491 def ImagePropFromGlobalDict(glob_dict, mount_point):
    492   """Build an image property dictionary from the global dictionary.
    493 
    494   Args:
    495     glob_dict: the global dictionary from the build system.
    496     mount_point: such as "system", "data" etc.
    497   """
    498   d = {}
    499 
    500   if "build.prop" in glob_dict:
    501     bp = glob_dict["build.prop"]
    502     if "ro.build.date.utc" in bp:
    503       d["timestamp"] = bp["ro.build.date.utc"]
    504 
    505   def copy_prop(src_p, dest_p):
    506     """Copy a property from the global dictionary.
    507 
    508     Args:
    509       src_p: The source property in the global dictionary.
    510       dest_p: The destination property.
    511     Returns:
    512       True if property was found and copied, False otherwise.
    513     """
    514     if src_p in glob_dict:
    515       d[dest_p] = str(glob_dict[src_p])
    516       return True
    517     return False
    518 
    519   common_props = (
    520       "extfs_sparse_flag",
    521       "squashfs_sparse_flag",
    522       "selinux_fc",
    523       "skip_fsck",
    524       "ext_mkuserimg",
    525       "verity",
    526       "verity_key",
    527       "verity_signer_cmd",
    528       "verity_fec",
    529       "verity_disable",
    530       "avb_enable",
    531       "avb_avbtool",
    532       "avb_salt",
    533       "use_dynamic_partition_size",
    534   )
    535   for p in common_props:
    536     copy_prop(p, p)
    537 
    538   d["mount_point"] = mount_point
    539   if mount_point == "system":
    540     copy_prop("avb_system_hashtree_enable", "avb_hashtree_enable")
    541     copy_prop("avb_system_add_hashtree_footer_args",
    542               "avb_add_hashtree_footer_args")
    543     copy_prop("avb_system_key_path", "avb_key_path")
    544     copy_prop("avb_system_algorithm", "avb_algorithm")
    545     copy_prop("fs_type", "fs_type")
    546     # Copy the generic system fs type first, override with specific one if
    547     # available.
    548     copy_prop("system_fs_type", "fs_type")
    549     copy_prop("system_headroom", "partition_headroom")
    550     copy_prop("system_size", "partition_size")
    551     if not copy_prop("system_journal_size", "journal_size"):
    552       d["journal_size"] = "0"
    553     copy_prop("system_verity_block_device", "verity_block_device")
    554     copy_prop("system_root_image", "system_root_image")
    555     copy_prop("root_dir", "root_dir")
    556     copy_prop("root_fs_config", "root_fs_config")
    557     copy_prop("ext4_share_dup_blocks", "ext4_share_dup_blocks")
    558     copy_prop("system_squashfs_compressor", "squashfs_compressor")
    559     copy_prop("system_squashfs_compressor_opt", "squashfs_compressor_opt")
    560     copy_prop("system_squashfs_block_size", "squashfs_block_size")
    561     copy_prop("system_squashfs_disable_4k_align", "squashfs_disable_4k_align")
    562     copy_prop("system_base_fs_file", "base_fs_file")
    563     copy_prop("system_extfs_inode_count", "extfs_inode_count")
    564     if not copy_prop("system_extfs_rsv_pct", "extfs_rsv_pct"):
    565       d["extfs_rsv_pct"] = "0"
    566     copy_prop("system_reserved_size", "partition_reserved_size")
    567   elif mount_point == "system_other":
    568     # We inherit the selinux policies of /system since we contain some of its
    569     # files.
    570     copy_prop("avb_system_other_hashtree_enable", "avb_hashtree_enable")
    571     copy_prop("avb_system_other_add_hashtree_footer_args",
    572               "avb_add_hashtree_footer_args")
    573     copy_prop("avb_system_other_key_path", "avb_key_path")
    574     copy_prop("avb_system_other_algorithm", "avb_algorithm")
    575     copy_prop("fs_type", "fs_type")
    576     copy_prop("system_fs_type", "fs_type")
    577     copy_prop("system_other_size", "partition_size")
    578     if not copy_prop("system_journal_size", "journal_size"):
    579       d["journal_size"] = "0"
    580     copy_prop("system_verity_block_device", "verity_block_device")
    581     copy_prop("ext4_share_dup_blocks", "ext4_share_dup_blocks")
    582     copy_prop("system_squashfs_compressor", "squashfs_compressor")
    583     copy_prop("system_squashfs_compressor_opt", "squashfs_compressor_opt")
    584     copy_prop("system_squashfs_block_size", "squashfs_block_size")
    585     copy_prop("system_base_fs_file", "base_fs_file")
    586     copy_prop("system_extfs_inode_count", "extfs_inode_count")
    587     if not copy_prop("system_extfs_rsv_pct", "extfs_rsv_pct"):
    588       d["extfs_rsv_pct"] = "0"
    589     copy_prop("system_reserved_size", "partition_reserved_size")
    590   elif mount_point == "data":
    591     # Copy the generic fs type first, override with specific one if available.
    592     copy_prop("fs_type", "fs_type")
    593     copy_prop("userdata_fs_type", "fs_type")
    594     copy_prop("userdata_size", "partition_size")
    595     copy_prop("flash_logical_block_size", "flash_logical_block_size")
    596     copy_prop("flash_erase_block_size", "flash_erase_block_size")
    597   elif mount_point == "cache":
    598     copy_prop("cache_fs_type", "fs_type")
    599     copy_prop("cache_size", "partition_size")
    600   elif mount_point == "vendor":
    601     copy_prop("avb_vendor_hashtree_enable", "avb_hashtree_enable")
    602     copy_prop("avb_vendor_add_hashtree_footer_args",
    603               "avb_add_hashtree_footer_args")
    604     copy_prop("avb_vendor_key_path", "avb_key_path")
    605     copy_prop("avb_vendor_algorithm", "avb_algorithm")
    606     copy_prop("vendor_fs_type", "fs_type")
    607     copy_prop("vendor_size", "partition_size")
    608     if not copy_prop("vendor_journal_size", "journal_size"):
    609       d["journal_size"] = "0"
    610     copy_prop("vendor_verity_block_device", "verity_block_device")
    611     copy_prop("ext4_share_dup_blocks", "ext4_share_dup_blocks")
    612     copy_prop("vendor_squashfs_compressor", "squashfs_compressor")
    613     copy_prop("vendor_squashfs_compressor_opt", "squashfs_compressor_opt")
    614     copy_prop("vendor_squashfs_block_size", "squashfs_block_size")
    615     copy_prop("vendor_squashfs_disable_4k_align", "squashfs_disable_4k_align")
    616     copy_prop("vendor_base_fs_file", "base_fs_file")
    617     copy_prop("vendor_extfs_inode_count", "extfs_inode_count")
    618     if not copy_prop("vendor_extfs_rsv_pct", "extfs_rsv_pct"):
    619       d["extfs_rsv_pct"] = "0"
    620     copy_prop("vendor_reserved_size", "partition_reserved_size")
    621   elif mount_point == "product":
    622     copy_prop("avb_product_hashtree_enable", "avb_hashtree_enable")
    623     copy_prop("avb_product_add_hashtree_footer_args",
    624               "avb_add_hashtree_footer_args")
    625     copy_prop("avb_product_key_path", "avb_key_path")
    626     copy_prop("avb_product_algorithm", "avb_algorithm")
    627     copy_prop("product_fs_type", "fs_type")
    628     copy_prop("product_size", "partition_size")
    629     if not copy_prop("product_journal_size", "journal_size"):
    630       d["journal_size"] = "0"
    631     copy_prop("product_verity_block_device", "verity_block_device")
    632     copy_prop("ext4_share_dup_blocks", "ext4_share_dup_blocks")
    633     copy_prop("product_squashfs_compressor", "squashfs_compressor")
    634     copy_prop("product_squashfs_compressor_opt", "squashfs_compressor_opt")
    635     copy_prop("product_squashfs_block_size", "squashfs_block_size")
    636     copy_prop("product_squashfs_disable_4k_align", "squashfs_disable_4k_align")
    637     copy_prop("product_base_fs_file", "base_fs_file")
    638     copy_prop("product_extfs_inode_count", "extfs_inode_count")
    639     if not copy_prop("product_extfs_rsv_pct", "extfs_rsv_pct"):
    640       d["extfs_rsv_pct"] = "0"
    641     copy_prop("product_reserved_size", "partition_reserved_size")
    642   elif mount_point == "product_services":
    643     copy_prop("avb_product_services_hashtree_enable", "avb_hashtree_enable")
    644     copy_prop("avb_product_services_add_hashtree_footer_args",
    645               "avb_add_hashtree_footer_args")
    646     copy_prop("avb_product_services_key_path", "avb_key_path")
    647     copy_prop("avb_product_services_algorithm", "avb_algorithm")
    648     copy_prop("product_services_fs_type", "fs_type")
    649     copy_prop("product_services_size", "partition_size")
    650     if not copy_prop("product_services_journal_size", "journal_size"):
    651       d["journal_size"] = "0"
    652     copy_prop("product_services_verity_block_device", "verity_block_device")
    653     copy_prop("ext4_share_dup_blocks", "ext4_share_dup_blocks")
    654     copy_prop("product_services_squashfs_compressor", "squashfs_compressor")
    655     copy_prop("product_services_squashfs_compressor_opt",
    656               "squashfs_compressor_opt")
    657     copy_prop("product_services_squashfs_block_size", "squashfs_block_size")
    658     copy_prop("product_services_squashfs_disable_4k_align",
    659               "squashfs_disable_4k_align")
    660     copy_prop("product_services_base_fs_file", "base_fs_file")
    661     copy_prop("product_services_extfs_inode_count", "extfs_inode_count")
    662     if not copy_prop("product_services_extfs_rsv_pct", "extfs_rsv_pct"):
    663       d["extfs_rsv_pct"] = "0"
    664     copy_prop("product_services_reserved_size", "partition_reserved_size")
    665   elif mount_point == "odm":
    666     copy_prop("avb_odm_hashtree_enable", "avb_hashtree_enable")
    667     copy_prop("avb_odm_add_hashtree_footer_args",
    668               "avb_add_hashtree_footer_args")
    669     copy_prop("avb_odm_key_path", "avb_key_path")
    670     copy_prop("avb_odm_algorithm", "avb_algorithm")
    671     copy_prop("odm_fs_type", "fs_type")
    672     copy_prop("odm_size", "partition_size")
    673     if not copy_prop("odm_journal_size", "journal_size"):
    674       d["journal_size"] = "0"
    675     copy_prop("odm_verity_block_device", "verity_block_device")
    676     copy_prop("ext4_share_dup_blocks", "ext4_share_dup_blocks")
    677     copy_prop("odm_squashfs_compressor", "squashfs_compressor")
    678     copy_prop("odm_squashfs_compressor_opt", "squashfs_compressor_opt")
    679     copy_prop("odm_squashfs_block_size", "squashfs_block_size")
    680     copy_prop("odm_squashfs_disable_4k_align", "squashfs_disable_4k_align")
    681     copy_prop("odm_base_fs_file", "base_fs_file")
    682     copy_prop("odm_extfs_inode_count", "extfs_inode_count")
    683     if not copy_prop("odm_extfs_rsv_pct", "extfs_rsv_pct"):
    684       d["extfs_rsv_pct"] = "0"
    685     copy_prop("odm_reserved_size", "partition_reserved_size")
    686   elif mount_point == "oem":
    687     copy_prop("fs_type", "fs_type")
    688     copy_prop("oem_size", "partition_size")
    689     if not copy_prop("oem_journal_size", "journal_size"):
    690       d["journal_size"] = "0"
    691     copy_prop("oem_extfs_inode_count", "extfs_inode_count")
    692     copy_prop("ext4_share_dup_blocks", "ext4_share_dup_blocks")
    693     if not copy_prop("oem_extfs_rsv_pct", "extfs_rsv_pct"):
    694       d["extfs_rsv_pct"] = "0"
    695   d["partition_name"] = mount_point
    696   return d
    697 
    698 
    699 def LoadGlobalDict(filename):
    700   """Load "name=value" pairs from filename"""
    701   d = {}
    702   f = open(filename)
    703   for line in f:
    704     line = line.strip()
    705     if not line or line.startswith("#"):
    706       continue
    707     k, v = line.split("=", 1)
    708     d[k] = v
    709   f.close()
    710   return d
    711 
    712 
    713 def GlobalDictFromImageProp(image_prop, mount_point):
    714   d = {}
    715   def copy_prop(src_p, dest_p):
    716     if src_p in image_prop:
    717       d[dest_p] = image_prop[src_p]
    718       return True
    719     return False
    720 
    721   if mount_point == "system":
    722     copy_prop("partition_size", "system_size")
    723   elif mount_point == "system_other":
    724     copy_prop("partition_size", "system_other_size")
    725   elif mount_point == "vendor":
    726     copy_prop("partition_size", "vendor_size")
    727   elif mount_point == "odm":
    728     copy_prop("partition_size", "odm_size")
    729   elif mount_point == "product":
    730     copy_prop("partition_size", "product_size")
    731   elif mount_point == "product_services":
    732     copy_prop("partition_size", "product_services_size")
    733   return d
    734 
    735 
    736 def main(argv):
    737   if len(argv) != 4:
    738     print(__doc__)
    739     sys.exit(1)
    740 
    741   common.InitLogging()
    742 
    743   in_dir = argv[0]
    744   glob_dict_file = argv[1]
    745   out_file = argv[2]
    746   target_out = argv[3]
    747 
    748   glob_dict = LoadGlobalDict(glob_dict_file)
    749   if "mount_point" in glob_dict:
    750     # The caller knows the mount point and provides a dictionary needed by
    751     # BuildImage().
    752     image_properties = glob_dict
    753   else:
    754     image_filename = os.path.basename(out_file)
    755     mount_point = ""
    756     if image_filename == "system.img":
    757       mount_point = "system"
    758     elif image_filename == "system_other.img":
    759       mount_point = "system_other"
    760     elif image_filename == "userdata.img":
    761       mount_point = "data"
    762     elif image_filename == "cache.img":
    763       mount_point = "cache"
    764     elif image_filename == "vendor.img":
    765       mount_point = "vendor"
    766     elif image_filename == "odm.img":
    767       mount_point = "odm"
    768     elif image_filename == "oem.img":
    769       mount_point = "oem"
    770     elif image_filename == "product.img":
    771       mount_point = "product"
    772     elif image_filename == "product_services.img":
    773       mount_point = "product_services"
    774     else:
    775       logger.error("Unknown image file name %s", image_filename)
    776       sys.exit(1)
    777 
    778     image_properties = ImagePropFromGlobalDict(glob_dict, mount_point)
    779 
    780   try:
    781     BuildImage(in_dir, image_properties, out_file, target_out)
    782   except:
    783     logger.error("Failed to build %s from %s", out_file, in_dir)
    784     raise
    785 
    786 
    787 if __name__ == '__main__':
    788   try:
    789     main(sys.argv[1:])
    790   finally:
    791     common.Cleanup()
    792