Loading tools/releasetools/add_img_to_target_files.py +4 −7 Original line number Diff line number Diff line Loading @@ -308,9 +308,8 @@ def CreateImage(input_dir, info_dict, what, output_file, block_list=None): hash_seed = "hash_seed-" + uuid_seed image_props["hash_seed"] = str(uuid.uuid5(uuid.NAMESPACE_URL, hash_seed)) succ = build_image.BuildImage(os.path.join(input_dir, what.upper()), image_props, output_file.name) assert succ, "build " + what + ".img image failed" build_image.BuildImage( os.path.join(input_dir, what.upper()), image_props, output_file.name) output_file.Write() if block_list: Loading Loading @@ -361,8 +360,7 @@ def AddUserdata(output_zip): fstab = OPTIONS.info_dict["fstab"] if fstab: image_props["fs_type"] = fstab["/data"].fs_type succ = build_image.BuildImage(user_dir, image_props, img.name) assert succ, "build userdata.img image failed" build_image.BuildImage(user_dir, image_props, img.name) common.CheckSize(img.name, "userdata.img", OPTIONS.info_dict) img.Write() Loading Loading @@ -514,8 +512,7 @@ def AddCache(output_zip): fstab = OPTIONS.info_dict["fstab"] if fstab: image_props["fs_type"] = fstab["/cache"].fs_type succ = build_image.BuildImage(user_dir, image_props, img.name) assert succ, "build cache.img image failed" build_image.BuildImage(user_dir, image_props, img.name) common.CheckSize(img.name, "cache.img", OPTIONS.info_dict) img.Write() Loading tools/releasetools/build_image.py +134 −148 Original line number Diff line number Diff line Loading @@ -45,6 +45,13 @@ BLOCK_SIZE = 4096 BYTES_IN_MB = 1024 * 1024 class BuildImageError(Exception): """An Exception raised during image building.""" def __init__(self, message): Exception.__init__(self, message) def RunCommand(cmd, verbose=None, env=None): """Echo and run the given command. Loading Loading @@ -76,58 +83,55 @@ def GetVerityFECSize(partition_size): cmd = ["fec", "-s", str(partition_size)] output, exit_code = RunCommand(cmd, False) if exit_code != 0: return False, 0 return True, int(output) raise BuildImageError("Failed to GetVerityFECSize:\n{}".format(output)) return int(output) def GetVerityTreeSize(partition_size): cmd = ["build_verity_tree", "-s", str(partition_size)] output, exit_code = RunCommand(cmd, False) if exit_code != 0: return False, 0 return True, int(output) raise BuildImageError("Failed to GetVerityTreeSize:\n{}".format(output)) return int(output) def GetVerityMetadataSize(partition_size): cmd = ["build_verity_metadata.py", "size", str(partition_size)] output, exit_code = RunCommand(cmd, False) if exit_code != 0: return False, 0 return True, int(output) raise BuildImageError("Failed to GetVerityMetadataSize:\n{}".format(output)) return int(output) def GetVeritySize(partition_size, fec_supported): success, verity_tree_size = GetVerityTreeSize(partition_size) if not success: return 0 success, verity_metadata_size = GetVerityMetadataSize(partition_size) if not success: return 0 verity_tree_size = GetVerityTreeSize(partition_size) verity_metadata_size = GetVerityMetadataSize(partition_size) verity_size = verity_tree_size + verity_metadata_size if fec_supported: success, fec_size = GetVerityFECSize(partition_size + verity_size) if not success: return 0 fec_size = GetVerityFECSize(partition_size + verity_size) return verity_size + fec_size return verity_size def GetDiskUsage(path): """Return number of bytes that "path" occupies on host. """Returns the number of bytes that "path" occupies on host. Args: path: The directory or file to calculate size on Returns: True and the number of bytes if successful, False and 0 otherwise. The number of bytes. Raises: BuildImageError: On error. """ env = {"POSIXLY_CORRECT": "1"} cmd = ["du", "-s", path] output, exit_code = RunCommand(cmd, verbose=False, env=env) if exit_code != 0: return False, 0 raise BuildImageError("Failed to get disk usage:\n{}".format(output)) # POSIX du returns number of blocks with block size 512 return True, int(output.split()[0]) * 512 return int(output.split()[0]) * 512 def GetSimgSize(image_file): Loading @@ -153,17 +157,24 @@ def AVBCalcMaxImageSize(avbtool, footer_type, partition_size, additional_args): or "avbtool add_hashtree_footer". Returns: The maximum image size or 0 if an error occurred. The maximum image size. Raises: BuildImageError: On error or getting invalid image size. """ cmd = [avbtool, "add_%s_footer" % footer_type, "--partition_size", str(partition_size), "--calc_max_image_size"] cmd.extend(shlex.split(additional_args)) (output, exit_code) = RunCommand(cmd) output, exit_code = RunCommand(cmd) if exit_code != 0: return 0 else: return int(output) raise BuildImageError( "Failed to calculate max image size:\n{}".format(output)) image_size = int(output) if image_size <= 0: raise BuildImageError( "Invalid max image size: {}".format(output)) return image_size def AVBCalcMinPartitionSize(image_size, size_calculator): Loading Loading @@ -240,8 +251,8 @@ def AVBAddFooter(image_path, avbtool, footer_type, partition_size, additional_args: Additional arguments to pass to "avbtool add_hash_footer" or "avbtool add_hashtree_footer". Returns: True if the operation succeeded. Raises: BuildImageError: On error. """ cmd = [avbtool, "add_%s_footer" % footer_type, "--partition_size", partition_size, Loading @@ -257,9 +268,8 @@ def AVBAddFooter(image_path, avbtool, footer_type, partition_size, output, exit_code = RunCommand(cmd) if exit_code != 0: print("Failed to add AVB footer! Error: %s" % output) return False return True raise BuildImageError( "Failed to add AVB footer:\n{}".format(output)) def AdjustPartitionSizeForVerity(partition_size, fec_supported): Loading Loading @@ -316,9 +326,8 @@ def BuildVerityFEC(sparse_image_path, verity_path, verity_fec_path, verity_path, verity_fec_path] output, exit_code = RunCommand(cmd) if exit_code != 0: print("Could not build FEC data! Error: %s" % output) return False return True raise BuildImageError( "Failed to build FEC data:\n{}".format(output)) def BuildVerityTree(sparse_image_path, verity_image_path, prop_dict): Loading @@ -326,12 +335,11 @@ def BuildVerityTree(sparse_image_path, verity_image_path, prop_dict): verity_image_path] output, exit_code = RunCommand(cmd) if exit_code != 0: print("Could not build verity tree! Error: %s" % output) return False raise BuildImageError( "Failed to build verity tree:\n{}".format(output)) root, salt = output.split() prop_dict["verity_root_hash"] = root prop_dict["verity_salt"] = salt return True def BuildVerityMetadata(image_size, verity_metadata_path, root_hash, salt, Loading @@ -345,9 +353,8 @@ def BuildVerityMetadata(image_size, verity_metadata_path, root_hash, salt, cmd.append("--verity_disable") output, exit_code = RunCommand(cmd) if exit_code != 0: print("Could not build verity metadata! Error: %s" % output) return False return True raise BuildImageError( "Failed to build verity metadata:\n{}".format(output)) def Append2Simg(sparse_image_path, unsparse_image_path, error_message): Loading @@ -356,49 +363,45 @@ def Append2Simg(sparse_image_path, unsparse_image_path, error_message): Args: sparse_image_path: the path to the (sparse) image unsparse_image_path: the path to the (unsparse) image Returns: True on success, False on failure. Raises: BuildImageError: On error. """ cmd = ["append2simg", sparse_image_path, unsparse_image_path] output, exit_code = RunCommand(cmd) if exit_code != 0: print("%s: %s" % (error_message, output)) return False return True raise BuildImageError("{}:\n{}".format(error_message, output)) def Append(target, file_to_append, error_message): """Appends file_to_append to target.""" """Appends file_to_append to target. Raises: BuildImageError: On error. """ try: with open(target, "a") as out_file, open(file_to_append, "r") as input_file: for line in input_file: out_file.write(line) except IOError: print(error_message) return False return True raise BuildImageError(error_message) def BuildVerifiedImage(data_image_path, verity_image_path, verity_metadata_path, verity_fec_path, padding_size, fec_supported): if not Append(verity_image_path, verity_metadata_path, "Could not append verity metadata!"): return False Append( verity_image_path, verity_metadata_path, "Could not append verity metadata!") if fec_supported: # build FEC for the entire partition, including metadata if not BuildVerityFEC(data_image_path, verity_image_path, verity_fec_path, padding_size): return False if not Append(verity_image_path, verity_fec_path, "Could not append FEC!"): return False # Build FEC for the entire partition, including metadata. BuildVerityFEC( data_image_path, verity_image_path, verity_fec_path, padding_size) Append(verity_image_path, verity_fec_path, "Could not append FEC!") if not Append2Simg(data_image_path, verity_image_path, "Could not append verity data!"): return False return True Append2Simg( data_image_path, verity_image_path, "Could not append verity data!") def UnsparseImage(sparse_image_path, replace=True): Loading @@ -409,15 +412,15 @@ def UnsparseImage(sparse_image_path, replace=True): if replace: os.unlink(unsparse_image_path) else: return True, unsparse_image_path return unsparse_image_path inflate_command = ["simg2img", sparse_image_path, unsparse_image_path] (inflate_output, exit_code) = RunCommand(inflate_command) inflate_output, exit_code = RunCommand(inflate_command) if exit_code != 0: print("Error: '%s' failed with exit code %d:\n%s" % ( inflate_command, exit_code, inflate_output)) os.remove(unsparse_image_path) return False, None return True, unsparse_image_path raise BuildImageError( "Error: '{}' failed with exit code {}:\n{}".format( inflate_command, exit_code, inflate_output)) return unsparse_image_path def MakeVerityEnabledImage(out_file, fec_supported, prop_dict): Loading @@ -427,8 +430,10 @@ def MakeVerityEnabledImage(out_file, fec_supported, prop_dict): out_file: the location to write the verifiable image at prop_dict: a dictionary of properties required for image creation and verification Returns: True on success, False otherwise. Raises: AssertionError: On invalid partition sizes. BuildImageError: On other errors. """ # get properties image_size = int(prop_dict["image_size"]) Loading @@ -440,50 +445,44 @@ def MakeVerityEnabledImage(out_file, fec_supported, prop_dict): signer_path = prop_dict["verity_signer_cmd"] signer_args = OPTIONS.verity_signer_args # make a tempdir tempdir_name = common.MakeTempDir(suffix="_verity_images") # get partial image paths # Get partial image paths. verity_image_path = os.path.join(tempdir_name, "verity.img") verity_metadata_path = os.path.join(tempdir_name, "verity_metadata.img") verity_fec_path = os.path.join(tempdir_name, "verity_fec.img") # build the verity tree and get the root hash and salt if not BuildVerityTree(out_file, verity_image_path, prop_dict): return False # Build the verity tree and get the root hash and salt. BuildVerityTree(out_file, verity_image_path, prop_dict) # build the metadata blocks # Build the metadata blocks. root_hash = prop_dict["verity_root_hash"] salt = prop_dict["verity_salt"] verity_disable = "verity_disable" in prop_dict if not BuildVerityMetadata(image_size, verity_metadata_path, root_hash, salt, block_dev, signer_path, signer_key, signer_args, verity_disable): return False BuildVerityMetadata( image_size, verity_metadata_path, root_hash, salt, block_dev, signer_path, signer_key, signer_args, verity_disable) # build the full verified image # Build the full verified image. partition_size = int(prop_dict["partition_size"]) verity_size = int(prop_dict["verity_size"]) padding_size = partition_size - image_size - verity_size assert padding_size >= 0 if not BuildVerifiedImage(out_file, verity_image_path, verity_metadata_path, verity_fec_path, padding_size, fec_supported): return False return True BuildVerifiedImage( out_file, verity_image_path, verity_metadata_path, verity_fec_path, padding_size, fec_supported) def ConvertBlockMapToBaseFs(block_map_file): base_fs_file = common.MakeTempFile(prefix="script_gen_", suffix=".base_fs") convert_command = ["blk_alloc_to_base_fs", block_map_file, base_fs_file] (_, exit_code) = RunCommand(convert_command) return base_fs_file if exit_code == 0 else None output, exit_code = RunCommand(convert_command) if exit_code != 0: raise BuildImageError( "Failed to call blk_alloc_to_base_fs:\n{}".format(output)) return base_fs_file def SetUpInDirAndFsConfig(origin_in, prop_dict): Loading Loading @@ -547,11 +546,9 @@ def CheckHeadroom(ext4fs_output, prop_dict): ext4fs_output: The output string from mke2fs command. prop_dict: The property dict. Returns: The check result. Raises: AssertionError: On invalid input. BuildImageError: On check failure. """ assert ext4fs_output is not None assert prop_dict.get('fs_type', '').startswith('ext4') Loading @@ -569,12 +566,11 @@ def CheckHeadroom(ext4fs_output, prop_dict): adjusted_blocks = total_blocks - headroom_blocks if used_blocks > adjusted_blocks: mount_point = prop_dict["mount_point"] print("Error: Not enough room on %s (total: %d blocks, used: %d blocks, " "headroom: %d blocks, available: %d blocks)" % ( raise BuildImageError( "Error: Not enough room on {} (total: {} blocks, used: {} blocks, " "headroom: {} blocks, available: {} blocks)".format( mount_point, total_blocks, used_blocks, headroom_blocks, adjusted_blocks)) return False return True def BuildImage(in_dir, prop_dict, out_file, target_out=None): Loading @@ -590,8 +586,8 @@ def BuildImage(in_dir, prop_dict, out_file, target_out=None): under system/core/libcutils) reads device specific FS config files from there. Returns: True iff the image is built successfully. Raises: BuildImageError: On build image failures. """ in_dir, fs_config = SetUpInDirAndFsConfig(in_dir, prop_dict) Loading Loading @@ -620,10 +616,8 @@ def BuildImage(in_dir, prop_dict, out_file, target_out=None): 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 partition_size is not defined, use output of `du' + reserved_size. size = GetDiskUsage(in_dir) 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)) Loading @@ -647,8 +641,6 @@ def BuildImage(in_dir, prop_dict, out_file, target_out=None): partition_size = int(prop_dict.get("partition_size")) image_size, verity_size = AdjustPartitionSizeForVerity( partition_size, verity_fec_supported) if not image_size: return False prop_dict["image_size"] = str(image_size) prop_dict["verity_size"] = str(verity_size) Loading @@ -656,11 +648,8 @@ def BuildImage(in_dir, prop_dict, out_file, target_out=None): if avb_footer_type: partition_size = prop_dict["partition_size"] # avb_add_hash_footer_args or avb_add_hashtree_footer_args. max_image_size = AVBCalcMaxImageSize(avbtool, avb_footer_type, partition_size, avb_signing_args) if max_image_size <= 0: print("AVBCalcMaxImageSize is <= 0: %d" % max_image_size) return False max_image_size = AVBCalcMaxImageSize( avbtool, avb_footer_type, partition_size, avb_signing_args) prop_dict["image_size"] = str(max_image_size) if fs_type.startswith("ext"): Loading @@ -683,8 +672,6 @@ def BuildImage(in_dir, prop_dict, out_file, target_out=None): 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: Loading Loading @@ -742,16 +729,17 @@ def BuildImage(in_dir, prop_dict, out_file, target_out=None): 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 raise BuildImageError( "Error: unknown filesystem type: {}".format(fs_type)) (mkfs_output, exit_code) = RunCommand(build_command) 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" try: du = GetDiskUsage(in_dir) du_str = "{} bytes ({} MB)".format(du, du // BYTES_IN_MB) except BuildImageError as e: print(e, file=sys.stderr) du_str = "unknown" print( "Out of space? The tree size of {} is {}, with reserved space of {} " "bytes ({} MB).".format( Loading @@ -765,28 +753,29 @@ def BuildImage(in_dir, prop_dict, out_file, target_out=None): int(prop_dict["image_size"]) // BYTES_IN_MB, int(prop_dict["partition_size"]), int(prop_dict["partition_size"]) // BYTES_IN_MB)) return False raise BuildImageError( "Error: '{}' failed with exit code {}:\n{}".format( build_command, exit_code, mkfs_output)) # 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 CheckHeadroom(mkfs_output, prop_dict) if not fs_spans_partition: mount_point = prop_dict.get("mount_point") image_size = int(prop_dict["image_size"]) sparse_image_size = GetSimgSize(out_file) if sparse_image_size > image_size: print("Error: %s image size of %d is larger than partition size of " "%d" % (mount_point, sparse_image_size, image_size)) return False raise BuildImageError( "Error: {} image size of {} is larger than partition size of " "{}".format(mount_point, sparse_image_size, image_size)) if verity_supported and is_verity_partition: ZeroPadSimg(out_file, image_size - sparse_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 MakeVerityEnabledImage(out_file, verity_fec_supported, prop_dict) # Add AVB HASH or HASHTREE footer (metadata). if avb_footer_type: Loading @@ -796,30 +785,25 @@ def BuildImage(in_dir, prop_dict, out_file, target_out=None): key_path = prop_dict.get("avb_key_path") algorithm = prop_dict.get("avb_algorithm") salt = prop_dict.get("avb_salt") if not AVBAddFooter(out_file, avbtool, avb_footer_type, partition_size, partition_name, key_path, algorithm, salt, avb_signing_args): return False AVBAddFooter( out_file, avbtool, avb_footer_type, partition_size, partition_name, key_path, algorithm, salt, avb_signing_args) if run_e2fsck and prop_dict.get("skip_fsck") != "true": success, unsparse_image = UnsparseImage(out_file, replace=False) if not success: return False unsparse_image = UnsparseImage(out_file, replace=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) 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" % ( raise BuildImageError( "Error: '{}' failed with exit code {}:\n{}".format( e2fsck_command, exit_code, e2fsck_output)) return False return True def ImagePropFromGlobalDict(glob_dict, mount_point): Loading Loading @@ -1110,10 +1094,12 @@ def main(argv): image_properties = ImagePropFromGlobalDict(glob_dict, mount_point) if not BuildImage(in_dir, image_properties, out_file, target_out): print("error: failed to build %s from %s" % (out_file, in_dir), try: BuildImage(in_dir, image_properties, out_file, target_out) except: print("Error: Failed to build {} from {}".format(out_file, in_dir), file=sys.stderr) sys.exit(1) raise if prop_file_out: glob_dict_out = GlobalDictFromImageProp(image_properties, mount_point) Loading tools/releasetools/test_build_image.py +7 −6 Original line number Diff line number Diff line Loading @@ -22,8 +22,8 @@ import unittest import common from build_image import ( AVBCalcMinPartitionSize, BLOCK_SIZE, CheckHeadroom, RunCommand, SetUpInDirAndFsConfig) AVBCalcMinPartitionSize, BLOCK_SIZE, BuildImageError, CheckHeadroom, RunCommand, SetUpInDirAndFsConfig) class BuildImageTest(unittest.TestCase): Loading @@ -49,7 +49,7 @@ class BuildImageTest(unittest.TestCase): 'partition_headroom' : '4096000', 'mount_point' : 'system', } self.assertTrue(CheckHeadroom(self.EXT4FS_OUTPUT, prop_dict)) CheckHeadroom(self.EXT4FS_OUTPUT, prop_dict) def test_CheckHeadroom_InsufficientHeadroom(self): # Required headroom: 1001 blocks. Loading @@ -58,7 +58,8 @@ class BuildImageTest(unittest.TestCase): 'partition_headroom' : '4100096', 'mount_point' : 'system', } self.assertFalse(CheckHeadroom(self.EXT4FS_OUTPUT, prop_dict)) self.assertRaises( BuildImageError, CheckHeadroom, self.EXT4FS_OUTPUT, prop_dict) def test_CheckHeadroom_WrongFsType(self): prop_dict = { Loading Loading @@ -98,14 +99,14 @@ class BuildImageTest(unittest.TestCase): 'partition_headroom' : '40960', 'mount_point' : 'system', } self.assertTrue(CheckHeadroom(ext4fs_output, prop_dict)) CheckHeadroom(ext4fs_output, prop_dict) prop_dict = { 'fs_type' : 'ext4', 'partition_headroom' : '413696', 'mount_point' : 'system', } self.assertFalse(CheckHeadroom(ext4fs_output, prop_dict)) self.assertRaises(BuildImageError, CheckHeadroom, ext4fs_output, prop_dict) def test_SetUpInDirAndFsConfig_SystemRootImageTrue_NonSystem(self): prop_dict = { Loading tools/releasetools/test_validate_target_files.py +1 −2 Original line number Diff line number Diff line Loading @@ -139,8 +139,7 @@ class ValidateTargetFilesTest(unittest.TestCase): 'verity_signer_cmd' : 'verity_signer', 'verity_size' : str(verity_size), } self.assertTrue( build_image.MakeVerityEnabledImage(output_file, verity_fec, prop_dict)) build_image.MakeVerityEnabledImage(output_file, verity_fec, prop_dict) def test_ValidateVerifiedBootImages_systemImage(self): input_tmp = common.MakeTempDir() Loading Loading
tools/releasetools/add_img_to_target_files.py +4 −7 Original line number Diff line number Diff line Loading @@ -308,9 +308,8 @@ def CreateImage(input_dir, info_dict, what, output_file, block_list=None): hash_seed = "hash_seed-" + uuid_seed image_props["hash_seed"] = str(uuid.uuid5(uuid.NAMESPACE_URL, hash_seed)) succ = build_image.BuildImage(os.path.join(input_dir, what.upper()), image_props, output_file.name) assert succ, "build " + what + ".img image failed" build_image.BuildImage( os.path.join(input_dir, what.upper()), image_props, output_file.name) output_file.Write() if block_list: Loading Loading @@ -361,8 +360,7 @@ def AddUserdata(output_zip): fstab = OPTIONS.info_dict["fstab"] if fstab: image_props["fs_type"] = fstab["/data"].fs_type succ = build_image.BuildImage(user_dir, image_props, img.name) assert succ, "build userdata.img image failed" build_image.BuildImage(user_dir, image_props, img.name) common.CheckSize(img.name, "userdata.img", OPTIONS.info_dict) img.Write() Loading Loading @@ -514,8 +512,7 @@ def AddCache(output_zip): fstab = OPTIONS.info_dict["fstab"] if fstab: image_props["fs_type"] = fstab["/cache"].fs_type succ = build_image.BuildImage(user_dir, image_props, img.name) assert succ, "build cache.img image failed" build_image.BuildImage(user_dir, image_props, img.name) common.CheckSize(img.name, "cache.img", OPTIONS.info_dict) img.Write() Loading
tools/releasetools/build_image.py +134 −148 Original line number Diff line number Diff line Loading @@ -45,6 +45,13 @@ BLOCK_SIZE = 4096 BYTES_IN_MB = 1024 * 1024 class BuildImageError(Exception): """An Exception raised during image building.""" def __init__(self, message): Exception.__init__(self, message) def RunCommand(cmd, verbose=None, env=None): """Echo and run the given command. Loading Loading @@ -76,58 +83,55 @@ def GetVerityFECSize(partition_size): cmd = ["fec", "-s", str(partition_size)] output, exit_code = RunCommand(cmd, False) if exit_code != 0: return False, 0 return True, int(output) raise BuildImageError("Failed to GetVerityFECSize:\n{}".format(output)) return int(output) def GetVerityTreeSize(partition_size): cmd = ["build_verity_tree", "-s", str(partition_size)] output, exit_code = RunCommand(cmd, False) if exit_code != 0: return False, 0 return True, int(output) raise BuildImageError("Failed to GetVerityTreeSize:\n{}".format(output)) return int(output) def GetVerityMetadataSize(partition_size): cmd = ["build_verity_metadata.py", "size", str(partition_size)] output, exit_code = RunCommand(cmd, False) if exit_code != 0: return False, 0 return True, int(output) raise BuildImageError("Failed to GetVerityMetadataSize:\n{}".format(output)) return int(output) def GetVeritySize(partition_size, fec_supported): success, verity_tree_size = GetVerityTreeSize(partition_size) if not success: return 0 success, verity_metadata_size = GetVerityMetadataSize(partition_size) if not success: return 0 verity_tree_size = GetVerityTreeSize(partition_size) verity_metadata_size = GetVerityMetadataSize(partition_size) verity_size = verity_tree_size + verity_metadata_size if fec_supported: success, fec_size = GetVerityFECSize(partition_size + verity_size) if not success: return 0 fec_size = GetVerityFECSize(partition_size + verity_size) return verity_size + fec_size return verity_size def GetDiskUsage(path): """Return number of bytes that "path" occupies on host. """Returns the number of bytes that "path" occupies on host. Args: path: The directory or file to calculate size on Returns: True and the number of bytes if successful, False and 0 otherwise. The number of bytes. Raises: BuildImageError: On error. """ env = {"POSIXLY_CORRECT": "1"} cmd = ["du", "-s", path] output, exit_code = RunCommand(cmd, verbose=False, env=env) if exit_code != 0: return False, 0 raise BuildImageError("Failed to get disk usage:\n{}".format(output)) # POSIX du returns number of blocks with block size 512 return True, int(output.split()[0]) * 512 return int(output.split()[0]) * 512 def GetSimgSize(image_file): Loading @@ -153,17 +157,24 @@ def AVBCalcMaxImageSize(avbtool, footer_type, partition_size, additional_args): or "avbtool add_hashtree_footer". Returns: The maximum image size or 0 if an error occurred. The maximum image size. Raises: BuildImageError: On error or getting invalid image size. """ cmd = [avbtool, "add_%s_footer" % footer_type, "--partition_size", str(partition_size), "--calc_max_image_size"] cmd.extend(shlex.split(additional_args)) (output, exit_code) = RunCommand(cmd) output, exit_code = RunCommand(cmd) if exit_code != 0: return 0 else: return int(output) raise BuildImageError( "Failed to calculate max image size:\n{}".format(output)) image_size = int(output) if image_size <= 0: raise BuildImageError( "Invalid max image size: {}".format(output)) return image_size def AVBCalcMinPartitionSize(image_size, size_calculator): Loading Loading @@ -240,8 +251,8 @@ def AVBAddFooter(image_path, avbtool, footer_type, partition_size, additional_args: Additional arguments to pass to "avbtool add_hash_footer" or "avbtool add_hashtree_footer". Returns: True if the operation succeeded. Raises: BuildImageError: On error. """ cmd = [avbtool, "add_%s_footer" % footer_type, "--partition_size", partition_size, Loading @@ -257,9 +268,8 @@ def AVBAddFooter(image_path, avbtool, footer_type, partition_size, output, exit_code = RunCommand(cmd) if exit_code != 0: print("Failed to add AVB footer! Error: %s" % output) return False return True raise BuildImageError( "Failed to add AVB footer:\n{}".format(output)) def AdjustPartitionSizeForVerity(partition_size, fec_supported): Loading Loading @@ -316,9 +326,8 @@ def BuildVerityFEC(sparse_image_path, verity_path, verity_fec_path, verity_path, verity_fec_path] output, exit_code = RunCommand(cmd) if exit_code != 0: print("Could not build FEC data! Error: %s" % output) return False return True raise BuildImageError( "Failed to build FEC data:\n{}".format(output)) def BuildVerityTree(sparse_image_path, verity_image_path, prop_dict): Loading @@ -326,12 +335,11 @@ def BuildVerityTree(sparse_image_path, verity_image_path, prop_dict): verity_image_path] output, exit_code = RunCommand(cmd) if exit_code != 0: print("Could not build verity tree! Error: %s" % output) return False raise BuildImageError( "Failed to build verity tree:\n{}".format(output)) root, salt = output.split() prop_dict["verity_root_hash"] = root prop_dict["verity_salt"] = salt return True def BuildVerityMetadata(image_size, verity_metadata_path, root_hash, salt, Loading @@ -345,9 +353,8 @@ def BuildVerityMetadata(image_size, verity_metadata_path, root_hash, salt, cmd.append("--verity_disable") output, exit_code = RunCommand(cmd) if exit_code != 0: print("Could not build verity metadata! Error: %s" % output) return False return True raise BuildImageError( "Failed to build verity metadata:\n{}".format(output)) def Append2Simg(sparse_image_path, unsparse_image_path, error_message): Loading @@ -356,49 +363,45 @@ def Append2Simg(sparse_image_path, unsparse_image_path, error_message): Args: sparse_image_path: the path to the (sparse) image unsparse_image_path: the path to the (unsparse) image Returns: True on success, False on failure. Raises: BuildImageError: On error. """ cmd = ["append2simg", sparse_image_path, unsparse_image_path] output, exit_code = RunCommand(cmd) if exit_code != 0: print("%s: %s" % (error_message, output)) return False return True raise BuildImageError("{}:\n{}".format(error_message, output)) def Append(target, file_to_append, error_message): """Appends file_to_append to target.""" """Appends file_to_append to target. Raises: BuildImageError: On error. """ try: with open(target, "a") as out_file, open(file_to_append, "r") as input_file: for line in input_file: out_file.write(line) except IOError: print(error_message) return False return True raise BuildImageError(error_message) def BuildVerifiedImage(data_image_path, verity_image_path, verity_metadata_path, verity_fec_path, padding_size, fec_supported): if not Append(verity_image_path, verity_metadata_path, "Could not append verity metadata!"): return False Append( verity_image_path, verity_metadata_path, "Could not append verity metadata!") if fec_supported: # build FEC for the entire partition, including metadata if not BuildVerityFEC(data_image_path, verity_image_path, verity_fec_path, padding_size): return False if not Append(verity_image_path, verity_fec_path, "Could not append FEC!"): return False # Build FEC for the entire partition, including metadata. BuildVerityFEC( data_image_path, verity_image_path, verity_fec_path, padding_size) Append(verity_image_path, verity_fec_path, "Could not append FEC!") if not Append2Simg(data_image_path, verity_image_path, "Could not append verity data!"): return False return True Append2Simg( data_image_path, verity_image_path, "Could not append verity data!") def UnsparseImage(sparse_image_path, replace=True): Loading @@ -409,15 +412,15 @@ def UnsparseImage(sparse_image_path, replace=True): if replace: os.unlink(unsparse_image_path) else: return True, unsparse_image_path return unsparse_image_path inflate_command = ["simg2img", sparse_image_path, unsparse_image_path] (inflate_output, exit_code) = RunCommand(inflate_command) inflate_output, exit_code = RunCommand(inflate_command) if exit_code != 0: print("Error: '%s' failed with exit code %d:\n%s" % ( inflate_command, exit_code, inflate_output)) os.remove(unsparse_image_path) return False, None return True, unsparse_image_path raise BuildImageError( "Error: '{}' failed with exit code {}:\n{}".format( inflate_command, exit_code, inflate_output)) return unsparse_image_path def MakeVerityEnabledImage(out_file, fec_supported, prop_dict): Loading @@ -427,8 +430,10 @@ def MakeVerityEnabledImage(out_file, fec_supported, prop_dict): out_file: the location to write the verifiable image at prop_dict: a dictionary of properties required for image creation and verification Returns: True on success, False otherwise. Raises: AssertionError: On invalid partition sizes. BuildImageError: On other errors. """ # get properties image_size = int(prop_dict["image_size"]) Loading @@ -440,50 +445,44 @@ def MakeVerityEnabledImage(out_file, fec_supported, prop_dict): signer_path = prop_dict["verity_signer_cmd"] signer_args = OPTIONS.verity_signer_args # make a tempdir tempdir_name = common.MakeTempDir(suffix="_verity_images") # get partial image paths # Get partial image paths. verity_image_path = os.path.join(tempdir_name, "verity.img") verity_metadata_path = os.path.join(tempdir_name, "verity_metadata.img") verity_fec_path = os.path.join(tempdir_name, "verity_fec.img") # build the verity tree and get the root hash and salt if not BuildVerityTree(out_file, verity_image_path, prop_dict): return False # Build the verity tree and get the root hash and salt. BuildVerityTree(out_file, verity_image_path, prop_dict) # build the metadata blocks # Build the metadata blocks. root_hash = prop_dict["verity_root_hash"] salt = prop_dict["verity_salt"] verity_disable = "verity_disable" in prop_dict if not BuildVerityMetadata(image_size, verity_metadata_path, root_hash, salt, block_dev, signer_path, signer_key, signer_args, verity_disable): return False BuildVerityMetadata( image_size, verity_metadata_path, root_hash, salt, block_dev, signer_path, signer_key, signer_args, verity_disable) # build the full verified image # Build the full verified image. partition_size = int(prop_dict["partition_size"]) verity_size = int(prop_dict["verity_size"]) padding_size = partition_size - image_size - verity_size assert padding_size >= 0 if not BuildVerifiedImage(out_file, verity_image_path, verity_metadata_path, verity_fec_path, padding_size, fec_supported): return False return True BuildVerifiedImage( out_file, verity_image_path, verity_metadata_path, verity_fec_path, padding_size, fec_supported) def ConvertBlockMapToBaseFs(block_map_file): base_fs_file = common.MakeTempFile(prefix="script_gen_", suffix=".base_fs") convert_command = ["blk_alloc_to_base_fs", block_map_file, base_fs_file] (_, exit_code) = RunCommand(convert_command) return base_fs_file if exit_code == 0 else None output, exit_code = RunCommand(convert_command) if exit_code != 0: raise BuildImageError( "Failed to call blk_alloc_to_base_fs:\n{}".format(output)) return base_fs_file def SetUpInDirAndFsConfig(origin_in, prop_dict): Loading Loading @@ -547,11 +546,9 @@ def CheckHeadroom(ext4fs_output, prop_dict): ext4fs_output: The output string from mke2fs command. prop_dict: The property dict. Returns: The check result. Raises: AssertionError: On invalid input. BuildImageError: On check failure. """ assert ext4fs_output is not None assert prop_dict.get('fs_type', '').startswith('ext4') Loading @@ -569,12 +566,11 @@ def CheckHeadroom(ext4fs_output, prop_dict): adjusted_blocks = total_blocks - headroom_blocks if used_blocks > adjusted_blocks: mount_point = prop_dict["mount_point"] print("Error: Not enough room on %s (total: %d blocks, used: %d blocks, " "headroom: %d blocks, available: %d blocks)" % ( raise BuildImageError( "Error: Not enough room on {} (total: {} blocks, used: {} blocks, " "headroom: {} blocks, available: {} blocks)".format( mount_point, total_blocks, used_blocks, headroom_blocks, adjusted_blocks)) return False return True def BuildImage(in_dir, prop_dict, out_file, target_out=None): Loading @@ -590,8 +586,8 @@ def BuildImage(in_dir, prop_dict, out_file, target_out=None): under system/core/libcutils) reads device specific FS config files from there. Returns: True iff the image is built successfully. Raises: BuildImageError: On build image failures. """ in_dir, fs_config = SetUpInDirAndFsConfig(in_dir, prop_dict) Loading Loading @@ -620,10 +616,8 @@ def BuildImage(in_dir, prop_dict, out_file, target_out=None): 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 partition_size is not defined, use output of `du' + reserved_size. size = GetDiskUsage(in_dir) 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)) Loading @@ -647,8 +641,6 @@ def BuildImage(in_dir, prop_dict, out_file, target_out=None): partition_size = int(prop_dict.get("partition_size")) image_size, verity_size = AdjustPartitionSizeForVerity( partition_size, verity_fec_supported) if not image_size: return False prop_dict["image_size"] = str(image_size) prop_dict["verity_size"] = str(verity_size) Loading @@ -656,11 +648,8 @@ def BuildImage(in_dir, prop_dict, out_file, target_out=None): if avb_footer_type: partition_size = prop_dict["partition_size"] # avb_add_hash_footer_args or avb_add_hashtree_footer_args. max_image_size = AVBCalcMaxImageSize(avbtool, avb_footer_type, partition_size, avb_signing_args) if max_image_size <= 0: print("AVBCalcMaxImageSize is <= 0: %d" % max_image_size) return False max_image_size = AVBCalcMaxImageSize( avbtool, avb_footer_type, partition_size, avb_signing_args) prop_dict["image_size"] = str(max_image_size) if fs_type.startswith("ext"): Loading @@ -683,8 +672,6 @@ def BuildImage(in_dir, prop_dict, out_file, target_out=None): 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: Loading Loading @@ -742,16 +729,17 @@ def BuildImage(in_dir, prop_dict, out_file, target_out=None): 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 raise BuildImageError( "Error: unknown filesystem type: {}".format(fs_type)) (mkfs_output, exit_code) = RunCommand(build_command) 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" try: du = GetDiskUsage(in_dir) du_str = "{} bytes ({} MB)".format(du, du // BYTES_IN_MB) except BuildImageError as e: print(e, file=sys.stderr) du_str = "unknown" print( "Out of space? The tree size of {} is {}, with reserved space of {} " "bytes ({} MB).".format( Loading @@ -765,28 +753,29 @@ def BuildImage(in_dir, prop_dict, out_file, target_out=None): int(prop_dict["image_size"]) // BYTES_IN_MB, int(prop_dict["partition_size"]), int(prop_dict["partition_size"]) // BYTES_IN_MB)) return False raise BuildImageError( "Error: '{}' failed with exit code {}:\n{}".format( build_command, exit_code, mkfs_output)) # 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 CheckHeadroom(mkfs_output, prop_dict) if not fs_spans_partition: mount_point = prop_dict.get("mount_point") image_size = int(prop_dict["image_size"]) sparse_image_size = GetSimgSize(out_file) if sparse_image_size > image_size: print("Error: %s image size of %d is larger than partition size of " "%d" % (mount_point, sparse_image_size, image_size)) return False raise BuildImageError( "Error: {} image size of {} is larger than partition size of " "{}".format(mount_point, sparse_image_size, image_size)) if verity_supported and is_verity_partition: ZeroPadSimg(out_file, image_size - sparse_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 MakeVerityEnabledImage(out_file, verity_fec_supported, prop_dict) # Add AVB HASH or HASHTREE footer (metadata). if avb_footer_type: Loading @@ -796,30 +785,25 @@ def BuildImage(in_dir, prop_dict, out_file, target_out=None): key_path = prop_dict.get("avb_key_path") algorithm = prop_dict.get("avb_algorithm") salt = prop_dict.get("avb_salt") if not AVBAddFooter(out_file, avbtool, avb_footer_type, partition_size, partition_name, key_path, algorithm, salt, avb_signing_args): return False AVBAddFooter( out_file, avbtool, avb_footer_type, partition_size, partition_name, key_path, algorithm, salt, avb_signing_args) if run_e2fsck and prop_dict.get("skip_fsck") != "true": success, unsparse_image = UnsparseImage(out_file, replace=False) if not success: return False unsparse_image = UnsparseImage(out_file, replace=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) 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" % ( raise BuildImageError( "Error: '{}' failed with exit code {}:\n{}".format( e2fsck_command, exit_code, e2fsck_output)) return False return True def ImagePropFromGlobalDict(glob_dict, mount_point): Loading Loading @@ -1110,10 +1094,12 @@ def main(argv): image_properties = ImagePropFromGlobalDict(glob_dict, mount_point) if not BuildImage(in_dir, image_properties, out_file, target_out): print("error: failed to build %s from %s" % (out_file, in_dir), try: BuildImage(in_dir, image_properties, out_file, target_out) except: print("Error: Failed to build {} from {}".format(out_file, in_dir), file=sys.stderr) sys.exit(1) raise if prop_file_out: glob_dict_out = GlobalDictFromImageProp(image_properties, mount_point) Loading
tools/releasetools/test_build_image.py +7 −6 Original line number Diff line number Diff line Loading @@ -22,8 +22,8 @@ import unittest import common from build_image import ( AVBCalcMinPartitionSize, BLOCK_SIZE, CheckHeadroom, RunCommand, SetUpInDirAndFsConfig) AVBCalcMinPartitionSize, BLOCK_SIZE, BuildImageError, CheckHeadroom, RunCommand, SetUpInDirAndFsConfig) class BuildImageTest(unittest.TestCase): Loading @@ -49,7 +49,7 @@ class BuildImageTest(unittest.TestCase): 'partition_headroom' : '4096000', 'mount_point' : 'system', } self.assertTrue(CheckHeadroom(self.EXT4FS_OUTPUT, prop_dict)) CheckHeadroom(self.EXT4FS_OUTPUT, prop_dict) def test_CheckHeadroom_InsufficientHeadroom(self): # Required headroom: 1001 blocks. Loading @@ -58,7 +58,8 @@ class BuildImageTest(unittest.TestCase): 'partition_headroom' : '4100096', 'mount_point' : 'system', } self.assertFalse(CheckHeadroom(self.EXT4FS_OUTPUT, prop_dict)) self.assertRaises( BuildImageError, CheckHeadroom, self.EXT4FS_OUTPUT, prop_dict) def test_CheckHeadroom_WrongFsType(self): prop_dict = { Loading Loading @@ -98,14 +99,14 @@ class BuildImageTest(unittest.TestCase): 'partition_headroom' : '40960', 'mount_point' : 'system', } self.assertTrue(CheckHeadroom(ext4fs_output, prop_dict)) CheckHeadroom(ext4fs_output, prop_dict) prop_dict = { 'fs_type' : 'ext4', 'partition_headroom' : '413696', 'mount_point' : 'system', } self.assertFalse(CheckHeadroom(ext4fs_output, prop_dict)) self.assertRaises(BuildImageError, CheckHeadroom, ext4fs_output, prop_dict) def test_SetUpInDirAndFsConfig_SystemRootImageTrue_NonSystem(self): prop_dict = { Loading
tools/releasetools/test_validate_target_files.py +1 −2 Original line number Diff line number Diff line Loading @@ -139,8 +139,7 @@ class ValidateTargetFilesTest(unittest.TestCase): 'verity_signer_cmd' : 'verity_signer', 'verity_size' : str(verity_size), } self.assertTrue( build_image.MakeVerityEnabledImage(output_file, verity_fec, prop_dict)) build_image.MakeVerityEnabledImage(output_file, verity_fec, prop_dict) def test_ValidateVerifiedBootImages_systemImage(self): input_tmp = common.MakeTempDir() Loading