Exemple #1
0
 def test_AVBCalcMinPartitionSize_LinearFooterSize(self):
   """Tests with footer size which is linear to partition size."""
   for image_size in self._image_sizes:
     for ratio in 0.95, 0.56, 0.22:
       expected_size = common.RoundUpTo4K(int(math.ceil(image_size / ratio)))
       self.assertEqual(
           expected_size,
           AVBCalcMinPartitionSize(image_size, lambda x: int(x * ratio)))
def _ReadFile(file_name, unpacked_name, round_up=False):
    """Constructs and returns a File object. Rounds up its size if needed."""
    assert os.path.exists(unpacked_name)
    with open(unpacked_name, 'rb') as f:
        file_data = f.read()
    file_size = len(file_data)
    if round_up:
        file_size_rounded_up = common.RoundUpTo4K(file_size)
        file_data += b'\0' * (file_size_rounded_up - file_size)
    return common.File(file_name, file_data)
 def test_CalculateMinPartitionSize_LinearFooterSize(self):
   """Tests with footer size which is linear to partition size."""
   image_sizes, builder = self._test_CalculateMinPartitionSize_SetUp()
   for image_size in image_sizes:
     for ratio in 0.95, 0.56, 0.22:
       expected_size = common.RoundUpTo4K(int(math.ceil(image_size / ratio)))
       self.assertEqual(
           expected_size,
           builder.CalculateMinPartitionSize(
               image_size, lambda x, ratio=ratio: int(x * ratio)))
Exemple #4
0
def BuildImage(in_dir, prop_dict, out_file, target_out=None):
    """Builds an image for the files under in_dir and writes it to out_file.

  Args:
    in_dir: Path to input directory.
    prop_dict: A property dict that contains info like partition size. Values
        will be updated with computed values.
    out_file: The output image file.
    target_out: Path to the TARGET_OUT directory as in Makefile. It actually
        points to the /system directory under PRODUCT_OUT. fs_config (the one
        under system/core/libcutils) reads device specific FS config files from
        there.

  Returns:
    True iff the image is built successfully.
  """
    in_dir, fs_config = SetUpInDirAndFsConfig(in_dir, prop_dict)

    build_command = []
    fs_type = prop_dict.get("fs_type", "")
    run_e2fsck = False

    fs_spans_partition = True
    if fs_type.startswith("squash"):
        fs_spans_partition = False

    is_verity_partition = "verity_block_device" in prop_dict
    verity_supported = prop_dict.get("verity") == "true"
    verity_fec_supported = prop_dict.get("verity_fec") == "true"

    if (prop_dict.get("use_dynamic_partition_size") == "true"
            and "partition_size" not in prop_dict):
        # if partition_size is not defined, use output of `du' + reserved_size
        success, size = GetDiskUsage(in_dir)
        if not success:
            return False
        if OPTIONS.verbose:
            print("The tree size of %s is %d MB." %
                  (in_dir, size // BYTES_IN_MB))
        size += int(prop_dict.get("partition_reserved_size", 0))
        # Round this up to a multiple of 4K so that avbtool works
        size = common.RoundUpTo4K(size)
        prop_dict["partition_size"] = str(size)
        if OPTIONS.verbose:
            print("Allocating %d MB for %s." % (size // BYTES_IN_MB, out_file))

    # Adjust the partition size to make room for the hashes if this is to be
    # verified.
    if verity_supported and is_verity_partition:
        partition_size = int(prop_dict.get("partition_size"))
        (adjusted_size,
         verity_size) = AdjustPartitionSizeForVerity(partition_size,
                                                     verity_fec_supported)
        if not adjusted_size:
            return False
        prop_dict["partition_size"] = str(adjusted_size)
        prop_dict["original_partition_size"] = str(partition_size)
        prop_dict["verity_size"] = str(verity_size)

    # Adjust partition size for AVB hash footer or AVB hashtree footer.
    avb_footer_type = ''
    if prop_dict.get("avb_hash_enable") == "true":
        avb_footer_type = 'hash'
    elif prop_dict.get("avb_hashtree_enable") == "true":
        avb_footer_type = 'hashtree'

    if avb_footer_type:
        avbtool = prop_dict["avb_avbtool"]
        partition_size = prop_dict["partition_size"]
        # avb_add_hash_footer_args or avb_add_hashtree_footer_args.
        additional_args = prop_dict["avb_add_" + avb_footer_type +
                                    "_footer_args"]
        max_image_size = AVBCalcMaxImageSize(avbtool, avb_footer_type,
                                             partition_size, additional_args)
        if max_image_size <= 0:
            print("AVBCalcMaxImageSize is <= 0: %d" % max_image_size)
            return False
        prop_dict["partition_size"] = str(max_image_size)
        prop_dict["original_partition_size"] = partition_size

    if fs_type.startswith("ext"):
        build_command = [prop_dict["ext_mkuserimg"]]
        if "extfs_sparse_flag" in prop_dict:
            build_command.append(prop_dict["extfs_sparse_flag"])
            run_e2fsck = True
        build_command.extend(
            [in_dir, out_file, fs_type, prop_dict["mount_point"]])
        build_command.append(prop_dict["partition_size"])
        if "journal_size" in prop_dict:
            build_command.extend(["-j", prop_dict["journal_size"]])
        if "timestamp" in prop_dict:
            build_command.extend(["-T", str(prop_dict["timestamp"])])
        if fs_config:
            build_command.extend(["-C", fs_config])
        if target_out:
            build_command.extend(["-D", target_out])
        if "block_list" in prop_dict:
            build_command.extend(["-B", prop_dict["block_list"]])
        if "base_fs_file" in prop_dict:
            base_fs_file = ConvertBlockMapToBaseFs(prop_dict["base_fs_file"])
            if base_fs_file is None:
                return False
            build_command.extend(["-d", base_fs_file])
        build_command.extend(["-L", prop_dict["mount_point"]])
        if "extfs_inode_count" in prop_dict:
            build_command.extend(["-i", prop_dict["extfs_inode_count"]])
        if "extfs_rsv_pct" in prop_dict:
            build_command.extend(["-M", prop_dict["extfs_rsv_pct"]])
        if "flash_erase_block_size" in prop_dict:
            build_command.extend(["-e", prop_dict["flash_erase_block_size"]])
        if "flash_logical_block_size" in prop_dict:
            build_command.extend(["-o", prop_dict["flash_logical_block_size"]])
        # Specify UUID and hash_seed if using mke2fs.
        if prop_dict["ext_mkuserimg"] == "mkuserimg_mke2fs":
            if "uuid" in prop_dict:
                build_command.extend(["-U", prop_dict["uuid"]])
            if "hash_seed" in prop_dict:
                build_command.extend(["-S", prop_dict["hash_seed"]])
        if "ext4_share_dup_blocks" in prop_dict:
            build_command.append("-c")
        if "selinux_fc" in prop_dict:
            build_command.append(prop_dict["selinux_fc"])
    elif fs_type.startswith("squash"):
        build_command = ["mksquashfsimage.sh"]
        build_command.extend([in_dir, out_file])
        if "squashfs_sparse_flag" in prop_dict:
            build_command.extend([prop_dict["squashfs_sparse_flag"]])
        build_command.extend(["-m", prop_dict["mount_point"]])
        if target_out:
            build_command.extend(["-d", target_out])
        if fs_config:
            build_command.extend(["-C", fs_config])
        if "selinux_fc" in prop_dict:
            build_command.extend(["-c", prop_dict["selinux_fc"]])
        if "block_list" in prop_dict:
            build_command.extend(["-B", prop_dict["block_list"]])
        if "squashfs_block_size" in prop_dict:
            build_command.extend(["-b", prop_dict["squashfs_block_size"]])
        if "squashfs_compressor" in prop_dict:
            build_command.extend(["-z", prop_dict["squashfs_compressor"]])
        if "squashfs_compressor_opt" in prop_dict:
            build_command.extend(["-zo", prop_dict["squashfs_compressor_opt"]])
        if prop_dict.get("squashfs_disable_4k_align") == "true":
            build_command.extend(["-a"])
    elif fs_type.startswith("f2fs"):
        build_command = ["mkf2fsuserimg.sh"]
        build_command.extend([out_file, prop_dict["partition_size"]])
        if fs_config:
            build_command.extend(["-C", fs_config])
        build_command.extend(["-f", in_dir])
        if target_out:
            build_command.extend(["-D", target_out])
        if "selinux_fc" in prop_dict:
            build_command.extend(["-s", prop_dict["selinux_fc"]])
        build_command.extend(["-t", prop_dict["mount_point"]])
        if "timestamp" in prop_dict:
            build_command.extend(["-T", str(prop_dict["timestamp"])])
        build_command.extend(["-L", prop_dict["mount_point"]])
    else:
        print("Error: unknown filesystem type '%s'" % (fs_type))
        return False

    (mkfs_output, exit_code) = RunCommand(build_command)
    if exit_code != 0:
        print("Error: '%s' failed with exit code %d:\n%s" %
              (build_command, exit_code, mkfs_output))
        success, du = GetDiskUsage(in_dir)
        du_str = ("%d bytes (%d MB)" %
                  (du, du // BYTES_IN_MB)) if success else "unknown"
        print(
            "Out of space? The tree size of {} is {}, with reserved space of {} "
            "bytes ({} MB).".format(
                in_dir, du_str, int(prop_dict.get("partition_reserved_size",
                                                  0)),
                int(prop_dict.get("partition_reserved_size", 0)) //
                BYTES_IN_MB))
        if "original_partition_size" in prop_dict:
            print(
                "The max size for filsystem files is {} bytes ({} MB), out of a "
                "total image size of {} bytes ({} MB).".format(
                    int(prop_dict["partition_size"]),
                    int(prop_dict["partition_size"]) // BYTES_IN_MB,
                    int(prop_dict["original_partition_size"]),
                    int(prop_dict["original_partition_size"]) // BYTES_IN_MB))
        else:
            print("The max image size is {} bytes ({} MB).".format(
                int(prop_dict["partition_size"]),
                int(prop_dict["partition_size"]) // BYTES_IN_MB))
        return False

    # Check if there's enough headroom space available for ext4 image.
    if "partition_headroom" in prop_dict and fs_type.startswith("ext4"):
        if not CheckHeadroom(mkfs_output, prop_dict):
            return False

    if not fs_spans_partition:
        mount_point = prop_dict.get("mount_point")
        partition_size = int(prop_dict.get("partition_size"))
        image_size = GetSimgSize(out_file)
        if image_size > partition_size:
            print(
                "Error: %s image size of %d is larger than partition size of "
                "%d" % (mount_point, image_size, partition_size))
            return False
        if verity_supported and is_verity_partition:
            ZeroPadSimg(out_file, partition_size - image_size)

    # Create the verified image if this is to be verified.
    if verity_supported and is_verity_partition:
        if not MakeVerityEnabledImage(out_file, verity_fec_supported,
                                      prop_dict):
            return False

    # Add AVB HASH or HASHTREE footer (metadata).
    if avb_footer_type:
        avbtool = prop_dict["avb_avbtool"]
        original_partition_size = prop_dict["original_partition_size"]
        partition_name = prop_dict["partition_name"]
        # key_path and algorithm are only available when chain partition is used.
        key_path = prop_dict.get("avb_key_path")
        algorithm = prop_dict.get("avb_algorithm")
        salt = prop_dict.get("avb_salt")
        # avb_add_hash_footer_args or avb_add_hashtree_footer_args
        additional_args = prop_dict["avb_add_" + avb_footer_type +
                                    "_footer_args"]
        if not AVBAddFooter(out_file, avbtool, avb_footer_type,
                            original_partition_size, partition_name, key_path,
                            algorithm, salt, additional_args):
            return False

    if run_e2fsck and prop_dict.get("skip_fsck") != "true":
        success, unsparse_image = UnsparseImage(out_file, replace=False)
        if not success:
            return False

        # Run e2fsck on the inflated image file
        e2fsck_command = ["e2fsck", "-f", "-n", unsparse_image]
        # TODO(b/112062612): work around e2fsck failure with SANITIZE_HOST=address
        env4e2fsck = {"ASAN_OPTIONS": "detect_odr_violation=0"}
        (e2fsck_output, exit_code) = RunCommand(e2fsck_command, env=env4e2fsck)

        os.remove(unsparse_image)

        if exit_code != 0:
            print("Error: '%s' failed with exit code %d:\n%s" %
                  (e2fsck_command, exit_code, e2fsck_output))
            return False

    return True
Exemple #5
0
def BuildImage(in_dir, prop_dict, out_file, target_out=None):
    """Builds an image for the files under in_dir and writes it to out_file.

  Args:
    in_dir: Path to input directory.
    prop_dict: A property dict that contains info like partition size. Values
        will be updated with computed values.
    out_file: The output image file.
    target_out: Path to the TARGET_OUT directory as in Makefile. It actually
        points to the /system directory under PRODUCT_OUT. fs_config (the one
        under system/core/libcutils) reads device specific FS config files from
        there.

  Raises:
    BuildImageError: On build image failures.
  """
    in_dir, fs_config = SetUpInDirAndFsConfig(in_dir, prop_dict)

    build_command = []
    fs_type = prop_dict.get("fs_type", "")

    fs_spans_partition = True
    if fs_type.startswith("squash"):
        fs_spans_partition = False

    # Get a builder for creating an image that's to be verified by Verified Boot,
    # or None if not applicable.
    verity_image_builder = verity_utils.CreateVerityImageBuilder(prop_dict)

    if (prop_dict.get("use_dynamic_partition_size") == "true"
            and "partition_size" not in prop_dict):
        # If partition_size is not defined, use output of `du' + reserved_size.
        size = GetDiskUsage(in_dir)
        logger.info("The tree size of %s is %d MB.", in_dir,
                    size // BYTES_IN_MB)
        # If not specified, give us 16MB margin for GetDiskUsage error ...
        reserved_size = int(
            prop_dict.get("partition_reserved_size", BYTES_IN_MB * 16))
        partition_headroom = int(prop_dict.get("partition_headroom", 0))
        if fs_type.startswith("ext4") and partition_headroom > reserved_size:
            reserved_size = partition_headroom
        size += reserved_size
        # Round this up to a multiple of 4K so that avbtool works
        size = common.RoundUpTo4K(size)
        if fs_type.startswith("ext"):
            prop_dict["partition_size"] = str(size)
            prop_dict["image_size"] = str(size)
            if "extfs_inode_count" not in prop_dict:
                prop_dict["extfs_inode_count"] = str(GetInodeUsage(in_dir))
            logger.info(
                "First Pass based on estimates of %d MB and %s inodes.",
                size // BYTES_IN_MB, prop_dict["extfs_inode_count"])
            BuildImageMkfs(in_dir, prop_dict, out_file, target_out, fs_config)
            sparse_image = False
            if "extfs_sparse_flag" in prop_dict:
                sparse_image = True
            fs_dict = GetFilesystemCharacteristics(out_file, sparse_image)
            os.remove(out_file)
            block_size = int(fs_dict.get("Block size", "4096"))
            free_size = int(fs_dict.get("Free blocks", "0")) * block_size
            reserved_size = int(prop_dict.get("partition_reserved_size", 0))
            partition_headroom = int(fs_dict.get("partition_headroom", 0))
            if fs_type.startswith(
                    "ext4") and partition_headroom > reserved_size:
                reserved_size = partition_headroom
            if free_size <= reserved_size:
                logger.info("Not worth reducing image %d <= %d.", free_size,
                            reserved_size)
            else:
                size -= free_size
                size += reserved_size
                if reserved_size == 0:
                    # add .3% margin
                    size = size * 1003 // 1000
                # Use a minimum size, otherwise we will fail to calculate an AVB footer
                # or fail to construct an ext4 image.
                size = max(size, 256 * 1024)
                if block_size <= 4096:
                    size = common.RoundUpTo4K(size)
                else:
                    size = ((size + block_size - 1) // block_size) * block_size
            extfs_inode_count = prop_dict["extfs_inode_count"]
            inodes = int(fs_dict.get("Inode count", extfs_inode_count))
            inodes -= int(fs_dict.get("Free inodes", "0"))
            # add .2% margin or 1 inode, whichever is greater
            spare_inodes = inodes * 2 // 1000
            min_spare_inodes = 1
            if spare_inodes < min_spare_inodes:
                spare_inodes = min_spare_inodes
            inodes += spare_inodes
            prop_dict["extfs_inode_count"] = str(inodes)
            prop_dict["partition_size"] = str(size)
            logger.info("Allocating %d Inodes for %s.", inodes, out_file)
        if verity_image_builder:
            size = verity_image_builder.CalculateDynamicPartitionSize(size)
        prop_dict["partition_size"] = str(size)
        logger.info("Allocating %d MB for %s.", size // BYTES_IN_MB, out_file)

    prop_dict["image_size"] = prop_dict["partition_size"]

    # Adjust the image size to make room for the hashes if this is to be verified.
    if verity_image_builder:
        max_image_size = verity_image_builder.CalculateMaxImageSize()
        prop_dict["image_size"] = str(max_image_size)

    mkfs_output = BuildImageMkfs(in_dir, prop_dict, out_file, target_out,
                                 fs_config)

    # Check if there's enough headroom space available for ext4 image.
    if "partition_headroom" in prop_dict and fs_type.startswith("ext4"):
        CheckHeadroom(mkfs_output, prop_dict)

    if not fs_spans_partition and verity_image_builder:
        verity_image_builder.PadSparseImage(out_file)

    # Create the verified image if this is to be verified.
    if verity_image_builder:
        verity_image_builder.Build(out_file)
Exemple #6
0
def BuildImage(in_dir, prop_dict, out_file, target_out=None):
    """Build an image to out_file from in_dir with property prop_dict.
  After the function call, values in prop_dict is updated with
  computed values.

  Args:
    in_dir: path of input directory.
    prop_dict: property dictionary.
    out_file: path of the output image file.
    target_out: path of the product out directory to read device specific FS
        config files.

  Returns:
    True iff the image is built successfully.
  """
    # system_root_image=true: build a system.img that combines the contents of
    # /system and root, which should be mounted at the root of the file system.
    origin_in = in_dir
    fs_config = prop_dict.get("fs_config")
    if (prop_dict.get("system_root_image") == "true"
            and prop_dict["mount_point"] == "system"):
        in_dir = common.MakeTempDir()
        # Change the mount point to "/".
        prop_dict["mount_point"] = "/"
        if fs_config:
            # We need to merge the fs_config files of system and root.
            merged_fs_config = common.MakeTempFile(prefix="merged_fs_config",
                                                   suffix=".txt")
            with open(merged_fs_config, "w") as fw:
                if "root_fs_config" in prop_dict:
                    with open(prop_dict["root_fs_config"]) as fr:
                        fw.writelines(fr.readlines())
                with open(fs_config) as fr:
                    fw.writelines(fr.readlines())
            fs_config = merged_fs_config

    build_command = []
    fs_type = prop_dict.get("fs_type", "")
    run_e2fsck = False

    fs_spans_partition = True
    if fs_type.startswith("squash"):
        fs_spans_partition = False

    is_verity_partition = "verity_block_device" in prop_dict
    verity_supported = prop_dict.get("verity") == "true"
    verity_fec_supported = prop_dict.get("verity_fec") == "true"

    if (prop_dict.get("use_dynamic_partition_size") == "true"
            and "partition_size" not in prop_dict):
        # if partition_size is not defined, use output of `du' + reserved_size
        success, size = GetDiskUsage(origin_in)
        if not success:
            return False
        if OPTIONS.verbose:
            print("The tree size of %s is %d MB." %
                  (origin_in, size // BYTES_IN_MB))
        size += int(prop_dict.get("partition_reserved_size", 0))
        # Round this up to a multiple of 4K so that avbtool works
        size = common.RoundUpTo4K(size)
        prop_dict["partition_size"] = str(size)
        if OPTIONS.verbose:
            print("Allocating %d MB for %s." % (size // BYTES_IN_MB, out_file))

    # Adjust the partition size to make room for the hashes if this is to be
    # verified.
    if verity_supported and is_verity_partition:
        partition_size = int(prop_dict.get("partition_size"))
        (adjusted_size,
         verity_size) = AdjustPartitionSizeForVerity(partition_size,
                                                     verity_fec_supported)
        if not adjusted_size:
            return False
        prop_dict["partition_size"] = str(adjusted_size)
        prop_dict["original_partition_size"] = str(partition_size)
        prop_dict["verity_size"] = str(verity_size)

    # Adjust partition size for AVB hash footer or AVB hashtree footer.
    avb_footer_type = ''
    if prop_dict.get("avb_hash_enable") == "true":
        avb_footer_type = 'hash'
    elif prop_dict.get("avb_hashtree_enable") == "true":
        avb_footer_type = 'hashtree'

    if avb_footer_type:
        avbtool = prop_dict["avb_avbtool"]
        partition_size = prop_dict["partition_size"]
        # avb_add_hash_footer_args or avb_add_hashtree_footer_args.
        additional_args = prop_dict["avb_add_" + avb_footer_type +
                                    "_footer_args"]
        max_image_size = AVBCalcMaxImageSize(avbtool, avb_footer_type,
                                             partition_size, additional_args)
        if max_image_size == 0:
            return False
        prop_dict["partition_size"] = str(max_image_size)
        prop_dict["original_partition_size"] = partition_size

    if fs_type.startswith("ext"):
        build_command = [prop_dict["ext_mkuserimg"]]
        if "extfs_sparse_flag" in prop_dict:
            build_command.append(prop_dict["extfs_sparse_flag"])
            run_e2fsck = True
        build_command.extend(
            [in_dir, out_file, fs_type, prop_dict["mount_point"]])
        build_command.append(prop_dict["partition_size"])
        if "journal_size" in prop_dict:
            build_command.extend(["-j", prop_dict["journal_size"]])
        if "timestamp" in prop_dict:
            build_command.extend(["-T", str(prop_dict["timestamp"])])
        if fs_config:
            build_command.extend(["-C", fs_config])
        if target_out:
            build_command.extend(["-D", target_out])
        if "block_list" in prop_dict:
            build_command.extend(["-B", prop_dict["block_list"]])
        if "base_fs_file" in prop_dict:
            base_fs_file = ConvertBlockMapToBaseFs(prop_dict["base_fs_file"])
            if base_fs_file is None:
                return False
            build_command.extend(["-d", base_fs_file])
        build_command.extend(["-L", prop_dict["mount_point"]])
        if "extfs_inode_count" in prop_dict:
            build_command.extend(["-i", prop_dict["extfs_inode_count"]])
        if "extfs_rsv_pct" in prop_dict:
            build_command.extend(["-M", prop_dict["extfs_rsv_pct"]])
        if "flash_erase_block_size" in prop_dict:
            build_command.extend(["-e", prop_dict["flash_erase_block_size"]])
        if "flash_logical_block_size" in prop_dict:
            build_command.extend(["-o", prop_dict["flash_logical_block_size"]])
        # Specify UUID and hash_seed if using mke2fs.
        if prop_dict["ext_mkuserimg"] == "mkuserimg_mke2fs.sh":
            if "uuid" in prop_dict:
                build_command.extend(["-U", prop_dict["uuid"]])
            if "hash_seed" in prop_dict:
                build_command.extend(["-S", prop_dict["hash_seed"]])
        if "ext4_share_dup_blocks" in prop_dict:
            build_command.append("-c")
        if "selinux_fc" in prop_dict:
            build_command.append(prop_dict["selinux_fc"])
    elif fs_type.startswith("squash"):
        build_command = ["mksquashfsimage.sh"]
        build_command.extend([in_dir, out_file])
        if "squashfs_sparse_flag" in prop_dict:
            build_command.extend([prop_dict["squashfs_sparse_flag"]])
        build_command.extend(["-m", prop_dict["mount_point"]])
        if target_out:
            build_command.extend(["-d", target_out])
        if fs_config:
            build_command.extend(["-C", fs_config])
        if "selinux_fc" in prop_dict:
            build_command.extend(["-c", prop_dict["selinux_fc"]])
        if "block_list" in prop_dict:
            build_command.extend(["-B", prop_dict["block_list"]])
        if "squashfs_block_size" in prop_dict:
            build_command.extend(["-b", prop_dict["squashfs_block_size"]])
        if "squashfs_compressor" in prop_dict:
            build_command.extend(["-z", prop_dict["squashfs_compressor"]])
        if "squashfs_compressor_opt" in prop_dict:
            build_command.extend(["-zo", prop_dict["squashfs_compressor_opt"]])
        if prop_dict.get("squashfs_disable_4k_align") == "true":
            build_command.extend(["-a"])
    elif fs_type.startswith("f2fs"):
        build_command = ["mkf2fsuserimg.sh"]
        build_command.extend([out_file, prop_dict["partition_size"]])
        if fs_config:
            build_command.extend(["-C", fs_config])
        build_command.extend(["-f", in_dir])
        if target_out:
            build_command.extend(["-D", target_out])
        if "selinux_fc" in prop_dict:
            build_command.extend(["-s", prop_dict["selinux_fc"]])
        build_command.extend(["-t", prop_dict["mount_point"]])
        if "timestamp" in prop_dict:
            build_command.extend(["-T", str(prop_dict["timestamp"])])
        build_command.extend(["-L", prop_dict["mount_point"]])
    else:
        print("Error: unknown filesystem type '%s'" % (fs_type))
        return False

    if in_dir != origin_in:
        # Construct a staging directory of the root file system.
        root_dir = prop_dict.get("root_dir")
        if root_dir:
            shutil.rmtree(in_dir)
            shutil.copytree(root_dir, in_dir, symlinks=True)
        staging_system = os.path.join(in_dir, "system")
        shutil.rmtree(staging_system, ignore_errors=True)
        shutil.copytree(origin_in, staging_system, symlinks=True)

    (mkfs_output, exit_code) = RunCommand(build_command)
    if exit_code != 0:
        print("Error: '%s' failed with exit code %d:\n%s" %
              (build_command, exit_code, mkfs_output))
        success, du = GetDiskUsage(origin_in)
        du_str = ("%d bytes (%d MB)" %
                  (du, du // BYTES_IN_MB)) if success else "unknown"
        print("Out of space? The tree size of %s is %s.\n" %
              (origin_in, du_str))
        print("The max is %d bytes (%d MB).\n" %
              (int(prop_dict["partition_size"]),
               int(prop_dict["partition_size"]) // BYTES_IN_MB))
        print(
            "Reserved space is %d bytes (%d MB).\n" %
            (int(prop_dict.get("partition_reserved_size", 0)),
             int(prop_dict.get("partition_reserved_size", 0)) // BYTES_IN_MB))
        return False

    # Check if there's enough headroom space available for ext4 image.
    if "partition_headroom" in prop_dict and fs_type.startswith("ext4"):
        if not CheckHeadroom(mkfs_output, prop_dict):
            return False

    if not fs_spans_partition:
        mount_point = prop_dict.get("mount_point")
        partition_size = int(prop_dict.get("partition_size"))
        image_size = GetSimgSize(out_file)
        if image_size > partition_size:
            print(
                "Error: %s image size of %d is larger than partition size of "
                "%d" % (mount_point, image_size, partition_size))
            return False
        if verity_supported and is_verity_partition:
            ZeroPadSimg(out_file, partition_size - image_size)

    # Create the verified image if this is to be verified.
    if verity_supported and is_verity_partition:
        if not MakeVerityEnabledImage(out_file, verity_fec_supported,
                                      prop_dict):
            return False

    # Add AVB HASH or HASHTREE footer (metadata).
    if avb_footer_type:
        avbtool = prop_dict["avb_avbtool"]
        original_partition_size = prop_dict["original_partition_size"]
        partition_name = prop_dict["partition_name"]
        # key_path and algorithm are only available when chain partition is used.
        key_path = prop_dict.get("avb_key_path")
        algorithm = prop_dict.get("avb_algorithm")
        salt = prop_dict.get("avb_salt")
        # avb_add_hash_footer_args or avb_add_hashtree_footer_args
        additional_args = prop_dict["avb_add_" + avb_footer_type +
                                    "_footer_args"]
        if not AVBAddFooter(out_file, avbtool, avb_footer_type,
                            original_partition_size, partition_name, key_path,
                            algorithm, salt, additional_args):
            return False

    if run_e2fsck and prop_dict.get("skip_fsck") != "true":
        success, unsparse_image = UnsparseImage(out_file, replace=False)
        if not success:
            return False

        # Run e2fsck on the inflated image file
        e2fsck_command = ["e2fsck", "-f", "-n", unsparse_image]
        (e2fsck_output, exit_code) = RunCommand(e2fsck_command)

        os.remove(unsparse_image)

        if exit_code != 0:
            print("Error: '%s' failed with exit code %d:\n%s" %
                  (e2fsck_command, exit_code, e2fsck_output))
            return False

    return True