releasetools: Raise on image building errors.
The image building functions in build_image.py have been returning (success, result) or special values to indicate the validity of the result. The same logic can be better expressed by raising exceptions instead, because a) using a special value relies on caller to check for that magic value; b) exceptions can carry additional messages other than a boolean does, e.g. the output from the failing command; c) caller can have cleaner code flow without explicitly checking for the validity of the result. This CL changes such functions to raise on errors. The majority of these functions are internal to build_image.py only, except for BuildImage() that has a few callers in add_img_to_target_files.py (which all die upon error anyway). Test: `m dist` Test: python -m unittest test_build_image Test: python -m unittest test_add_img_to_target_files Test: python -m unittest test_validate_target_files Test: Inject an error to the depended binaries (e.g. avbtool), and check that build_image.py exits with error messages. Change-Id: Ibe4d51e267756bb1a00fa9238a213f9d55fd9b58
This commit is contained in:
parent
943be51cf1
commit
c6bd70a5e6
4 changed files with 146 additions and 163 deletions
|
@ -308,9 +308,8 @@ def CreateImage(input_dir, info_dict, what, output_file, block_list=None):
|
||||||
hash_seed = "hash_seed-" + uuid_seed
|
hash_seed = "hash_seed-" + uuid_seed
|
||||||
image_props["hash_seed"] = str(uuid.uuid5(uuid.NAMESPACE_URL, hash_seed))
|
image_props["hash_seed"] = str(uuid.uuid5(uuid.NAMESPACE_URL, hash_seed))
|
||||||
|
|
||||||
succ = build_image.BuildImage(os.path.join(input_dir, what.upper()),
|
build_image.BuildImage(
|
||||||
image_props, output_file.name)
|
os.path.join(input_dir, what.upper()), image_props, output_file.name)
|
||||||
assert succ, "build " + what + ".img image failed"
|
|
||||||
|
|
||||||
output_file.Write()
|
output_file.Write()
|
||||||
if block_list:
|
if block_list:
|
||||||
|
@ -361,8 +360,7 @@ def AddUserdata(output_zip):
|
||||||
fstab = OPTIONS.info_dict["fstab"]
|
fstab = OPTIONS.info_dict["fstab"]
|
||||||
if fstab:
|
if fstab:
|
||||||
image_props["fs_type"] = fstab["/data"].fs_type
|
image_props["fs_type"] = fstab["/data"].fs_type
|
||||||
succ = build_image.BuildImage(user_dir, image_props, img.name)
|
build_image.BuildImage(user_dir, image_props, img.name)
|
||||||
assert succ, "build userdata.img image failed"
|
|
||||||
|
|
||||||
common.CheckSize(img.name, "userdata.img", OPTIONS.info_dict)
|
common.CheckSize(img.name, "userdata.img", OPTIONS.info_dict)
|
||||||
img.Write()
|
img.Write()
|
||||||
|
@ -514,8 +512,7 @@ def AddCache(output_zip):
|
||||||
fstab = OPTIONS.info_dict["fstab"]
|
fstab = OPTIONS.info_dict["fstab"]
|
||||||
if fstab:
|
if fstab:
|
||||||
image_props["fs_type"] = fstab["/cache"].fs_type
|
image_props["fs_type"] = fstab["/cache"].fs_type
|
||||||
succ = build_image.BuildImage(user_dir, image_props, img.name)
|
build_image.BuildImage(user_dir, image_props, img.name)
|
||||||
assert succ, "build cache.img image failed"
|
|
||||||
|
|
||||||
common.CheckSize(img.name, "cache.img", OPTIONS.info_dict)
|
common.CheckSize(img.name, "cache.img", OPTIONS.info_dict)
|
||||||
img.Write()
|
img.Write()
|
||||||
|
|
|
@ -45,6 +45,13 @@ BLOCK_SIZE = 4096
|
||||||
BYTES_IN_MB = 1024 * 1024
|
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):
|
def RunCommand(cmd, verbose=None, env=None):
|
||||||
"""Echo and run the given command.
|
"""Echo and run the given command.
|
||||||
|
|
||||||
|
@ -76,58 +83,55 @@ def GetVerityFECSize(partition_size):
|
||||||
cmd = ["fec", "-s", str(partition_size)]
|
cmd = ["fec", "-s", str(partition_size)]
|
||||||
output, exit_code = RunCommand(cmd, False)
|
output, exit_code = RunCommand(cmd, False)
|
||||||
if exit_code != 0:
|
if exit_code != 0:
|
||||||
return False, 0
|
raise BuildImageError("Failed to GetVerityFECSize:\n{}".format(output))
|
||||||
return True, int(output)
|
return int(output)
|
||||||
|
|
||||||
|
|
||||||
def GetVerityTreeSize(partition_size):
|
def GetVerityTreeSize(partition_size):
|
||||||
cmd = ["build_verity_tree", "-s", str(partition_size)]
|
cmd = ["build_verity_tree", "-s", str(partition_size)]
|
||||||
output, exit_code = RunCommand(cmd, False)
|
output, exit_code = RunCommand(cmd, False)
|
||||||
if exit_code != 0:
|
if exit_code != 0:
|
||||||
return False, 0
|
raise BuildImageError("Failed to GetVerityTreeSize:\n{}".format(output))
|
||||||
return True, int(output)
|
return int(output)
|
||||||
|
|
||||||
|
|
||||||
def GetVerityMetadataSize(partition_size):
|
def GetVerityMetadataSize(partition_size):
|
||||||
cmd = ["build_verity_metadata.py", "size", str(partition_size)]
|
cmd = ["build_verity_metadata.py", "size", str(partition_size)]
|
||||||
output, exit_code = RunCommand(cmd, False)
|
output, exit_code = RunCommand(cmd, False)
|
||||||
if exit_code != 0:
|
if exit_code != 0:
|
||||||
return False, 0
|
raise BuildImageError("Failed to GetVerityMetadataSize:\n{}".format(output))
|
||||||
return True, int(output)
|
return int(output)
|
||||||
|
|
||||||
|
|
||||||
def GetVeritySize(partition_size, fec_supported):
|
def GetVeritySize(partition_size, fec_supported):
|
||||||
success, verity_tree_size = GetVerityTreeSize(partition_size)
|
verity_tree_size = GetVerityTreeSize(partition_size)
|
||||||
if not success:
|
verity_metadata_size = GetVerityMetadataSize(partition_size)
|
||||||
return 0
|
|
||||||
success, verity_metadata_size = GetVerityMetadataSize(partition_size)
|
|
||||||
if not success:
|
|
||||||
return 0
|
|
||||||
verity_size = verity_tree_size + verity_metadata_size
|
verity_size = verity_tree_size + verity_metadata_size
|
||||||
if fec_supported:
|
if fec_supported:
|
||||||
success, fec_size = GetVerityFECSize(partition_size + verity_size)
|
fec_size = GetVerityFECSize(partition_size + verity_size)
|
||||||
if not success:
|
|
||||||
return 0
|
|
||||||
return verity_size + fec_size
|
return verity_size + fec_size
|
||||||
return verity_size
|
return verity_size
|
||||||
|
|
||||||
|
|
||||||
def GetDiskUsage(path):
|
def GetDiskUsage(path):
|
||||||
"""Return number of bytes that "path" occupies on host.
|
"""Returns the number of bytes that "path" occupies on host.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
path: The directory or file to calculate size on
|
path: The directory or file to calculate size on
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
True and the number of bytes if successful,
|
The number of bytes.
|
||||||
False and 0 otherwise.
|
|
||||||
|
Raises:
|
||||||
|
BuildImageError: On error.
|
||||||
"""
|
"""
|
||||||
env = {"POSIXLY_CORRECT": "1"}
|
env = {"POSIXLY_CORRECT": "1"}
|
||||||
cmd = ["du", "-s", path]
|
cmd = ["du", "-s", path]
|
||||||
output, exit_code = RunCommand(cmd, verbose=False, env=env)
|
output, exit_code = RunCommand(cmd, verbose=False, env=env)
|
||||||
if exit_code != 0:
|
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
|
# 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):
|
def GetSimgSize(image_file):
|
||||||
|
@ -153,17 +157,24 @@ def AVBCalcMaxImageSize(avbtool, footer_type, partition_size, additional_args):
|
||||||
or "avbtool add_hashtree_footer".
|
or "avbtool add_hashtree_footer".
|
||||||
|
|
||||||
Returns:
|
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,
|
cmd = [avbtool, "add_%s_footer" % footer_type,
|
||||||
"--partition_size", str(partition_size), "--calc_max_image_size"]
|
"--partition_size", str(partition_size), "--calc_max_image_size"]
|
||||||
cmd.extend(shlex.split(additional_args))
|
cmd.extend(shlex.split(additional_args))
|
||||||
|
|
||||||
(output, exit_code) = RunCommand(cmd)
|
output, exit_code = RunCommand(cmd)
|
||||||
if exit_code != 0:
|
if exit_code != 0:
|
||||||
return 0
|
raise BuildImageError(
|
||||||
else:
|
"Failed to calculate max image size:\n{}".format(output))
|
||||||
return int(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):
|
def AVBCalcMinPartitionSize(image_size, size_calculator):
|
||||||
|
@ -240,8 +251,8 @@ def AVBAddFooter(image_path, avbtool, footer_type, partition_size,
|
||||||
additional_args: Additional arguments to pass to "avbtool add_hash_footer"
|
additional_args: Additional arguments to pass to "avbtool add_hash_footer"
|
||||||
or "avbtool add_hashtree_footer".
|
or "avbtool add_hashtree_footer".
|
||||||
|
|
||||||
Returns:
|
Raises:
|
||||||
True if the operation succeeded.
|
BuildImageError: On error.
|
||||||
"""
|
"""
|
||||||
cmd = [avbtool, "add_%s_footer" % footer_type,
|
cmd = [avbtool, "add_%s_footer" % footer_type,
|
||||||
"--partition_size", partition_size,
|
"--partition_size", partition_size,
|
||||||
|
@ -257,9 +268,8 @@ def AVBAddFooter(image_path, avbtool, footer_type, partition_size,
|
||||||
|
|
||||||
output, exit_code = RunCommand(cmd)
|
output, exit_code = RunCommand(cmd)
|
||||||
if exit_code != 0:
|
if exit_code != 0:
|
||||||
print("Failed to add AVB footer! Error: %s" % output)
|
raise BuildImageError(
|
||||||
return False
|
"Failed to add AVB footer:\n{}".format(output))
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def AdjustPartitionSizeForVerity(partition_size, fec_supported):
|
def AdjustPartitionSizeForVerity(partition_size, fec_supported):
|
||||||
|
@ -316,9 +326,8 @@ def BuildVerityFEC(sparse_image_path, verity_path, verity_fec_path,
|
||||||
verity_path, verity_fec_path]
|
verity_path, verity_fec_path]
|
||||||
output, exit_code = RunCommand(cmd)
|
output, exit_code = RunCommand(cmd)
|
||||||
if exit_code != 0:
|
if exit_code != 0:
|
||||||
print("Could not build FEC data! Error: %s" % output)
|
raise BuildImageError(
|
||||||
return False
|
"Failed to build FEC data:\n{}".format(output))
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def BuildVerityTree(sparse_image_path, verity_image_path, prop_dict):
|
def BuildVerityTree(sparse_image_path, verity_image_path, prop_dict):
|
||||||
|
@ -326,12 +335,11 @@ def BuildVerityTree(sparse_image_path, verity_image_path, prop_dict):
|
||||||
verity_image_path]
|
verity_image_path]
|
||||||
output, exit_code = RunCommand(cmd)
|
output, exit_code = RunCommand(cmd)
|
||||||
if exit_code != 0:
|
if exit_code != 0:
|
||||||
print("Could not build verity tree! Error: %s" % output)
|
raise BuildImageError(
|
||||||
return False
|
"Failed to build verity tree:\n{}".format(output))
|
||||||
root, salt = output.split()
|
root, salt = output.split()
|
||||||
prop_dict["verity_root_hash"] = root
|
prop_dict["verity_root_hash"] = root
|
||||||
prop_dict["verity_salt"] = salt
|
prop_dict["verity_salt"] = salt
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def BuildVerityMetadata(image_size, verity_metadata_path, root_hash, salt,
|
def BuildVerityMetadata(image_size, verity_metadata_path, root_hash, salt,
|
||||||
|
@ -345,9 +353,8 @@ def BuildVerityMetadata(image_size, verity_metadata_path, root_hash, salt,
|
||||||
cmd.append("--verity_disable")
|
cmd.append("--verity_disable")
|
||||||
output, exit_code = RunCommand(cmd)
|
output, exit_code = RunCommand(cmd)
|
||||||
if exit_code != 0:
|
if exit_code != 0:
|
||||||
print("Could not build verity metadata! Error: %s" % output)
|
raise BuildImageError(
|
||||||
return False
|
"Failed to build verity metadata:\n{}".format(output))
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def Append2Simg(sparse_image_path, unsparse_image_path, error_message):
|
def Append2Simg(sparse_image_path, unsparse_image_path, error_message):
|
||||||
|
@ -356,49 +363,45 @@ def Append2Simg(sparse_image_path, unsparse_image_path, error_message):
|
||||||
Args:
|
Args:
|
||||||
sparse_image_path: the path to the (sparse) image
|
sparse_image_path: the path to the (sparse) image
|
||||||
unsparse_image_path: the path to the (unsparse) 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]
|
cmd = ["append2simg", sparse_image_path, unsparse_image_path]
|
||||||
output, exit_code = RunCommand(cmd)
|
output, exit_code = RunCommand(cmd)
|
||||||
if exit_code != 0:
|
if exit_code != 0:
|
||||||
print("%s: %s" % (error_message, output))
|
raise BuildImageError("{}:\n{}".format(error_message, output))
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def Append(target, file_to_append, error_message):
|
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:
|
try:
|
||||||
with open(target, "a") as out_file, open(file_to_append, "r") as input_file:
|
with open(target, "a") as out_file, open(file_to_append, "r") as input_file:
|
||||||
for line in input_file:
|
for line in input_file:
|
||||||
out_file.write(line)
|
out_file.write(line)
|
||||||
except IOError:
|
except IOError:
|
||||||
print(error_message)
|
raise BuildImageError(error_message)
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def BuildVerifiedImage(data_image_path, verity_image_path,
|
def BuildVerifiedImage(data_image_path, verity_image_path,
|
||||||
verity_metadata_path, verity_fec_path,
|
verity_metadata_path, verity_fec_path,
|
||||||
padding_size, fec_supported):
|
padding_size, fec_supported):
|
||||||
if not Append(verity_image_path, verity_metadata_path,
|
Append(
|
||||||
"Could not append verity metadata!"):
|
verity_image_path, verity_metadata_path,
|
||||||
return False
|
"Could not append verity metadata!")
|
||||||
|
|
||||||
if fec_supported:
|
if fec_supported:
|
||||||
# build FEC for the entire partition, including metadata
|
# Build FEC for the entire partition, including metadata.
|
||||||
if not BuildVerityFEC(data_image_path, verity_image_path,
|
BuildVerityFEC(
|
||||||
verity_fec_path, padding_size):
|
data_image_path, verity_image_path, verity_fec_path, padding_size)
|
||||||
return False
|
Append(verity_image_path, verity_fec_path, "Could not append FEC!")
|
||||||
|
|
||||||
if not Append(verity_image_path, verity_fec_path, "Could not append FEC!"):
|
Append2Simg(
|
||||||
return False
|
data_image_path, verity_image_path, "Could not append verity data!")
|
||||||
|
|
||||||
if not Append2Simg(data_image_path, verity_image_path,
|
|
||||||
"Could not append verity data!"):
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def UnsparseImage(sparse_image_path, replace=True):
|
def UnsparseImage(sparse_image_path, replace=True):
|
||||||
|
@ -409,15 +412,15 @@ def UnsparseImage(sparse_image_path, replace=True):
|
||||||
if replace:
|
if replace:
|
||||||
os.unlink(unsparse_image_path)
|
os.unlink(unsparse_image_path)
|
||||||
else:
|
else:
|
||||||
return True, unsparse_image_path
|
return unsparse_image_path
|
||||||
inflate_command = ["simg2img", sparse_image_path, 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:
|
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)
|
os.remove(unsparse_image_path)
|
||||||
return False, None
|
raise BuildImageError(
|
||||||
return True, unsparse_image_path
|
"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):
|
def MakeVerityEnabledImage(out_file, fec_supported, prop_dict):
|
||||||
|
@ -427,8 +430,10 @@ def MakeVerityEnabledImage(out_file, fec_supported, prop_dict):
|
||||||
out_file: the location to write the verifiable image at
|
out_file: the location to write the verifiable image at
|
||||||
prop_dict: a dictionary of properties required for image creation and
|
prop_dict: a dictionary of properties required for image creation and
|
||||||
verification
|
verification
|
||||||
Returns:
|
|
||||||
True on success, False otherwise.
|
Raises:
|
||||||
|
AssertionError: On invalid partition sizes.
|
||||||
|
BuildImageError: On other errors.
|
||||||
"""
|
"""
|
||||||
# get properties
|
# get properties
|
||||||
image_size = int(prop_dict["image_size"])
|
image_size = int(prop_dict["image_size"])
|
||||||
|
@ -440,50 +445,44 @@ def MakeVerityEnabledImage(out_file, fec_supported, prop_dict):
|
||||||
signer_path = prop_dict["verity_signer_cmd"]
|
signer_path = prop_dict["verity_signer_cmd"]
|
||||||
signer_args = OPTIONS.verity_signer_args
|
signer_args = OPTIONS.verity_signer_args
|
||||||
|
|
||||||
# make a tempdir
|
|
||||||
tempdir_name = common.MakeTempDir(suffix="_verity_images")
|
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_image_path = os.path.join(tempdir_name, "verity.img")
|
||||||
verity_metadata_path = os.path.join(tempdir_name, "verity_metadata.img")
|
verity_metadata_path = os.path.join(tempdir_name, "verity_metadata.img")
|
||||||
verity_fec_path = os.path.join(tempdir_name, "verity_fec.img")
|
verity_fec_path = os.path.join(tempdir_name, "verity_fec.img")
|
||||||
|
|
||||||
# build the verity tree and get the root hash and salt
|
# Build the verity tree and get the root hash and salt.
|
||||||
if not BuildVerityTree(out_file, verity_image_path, prop_dict):
|
BuildVerityTree(out_file, verity_image_path, prop_dict)
|
||||||
return False
|
|
||||||
|
|
||||||
# build the metadata blocks
|
# Build the metadata blocks.
|
||||||
root_hash = prop_dict["verity_root_hash"]
|
root_hash = prop_dict["verity_root_hash"]
|
||||||
salt = prop_dict["verity_salt"]
|
salt = prop_dict["verity_salt"]
|
||||||
verity_disable = "verity_disable" in prop_dict
|
verity_disable = "verity_disable" in prop_dict
|
||||||
if not BuildVerityMetadata(image_size, verity_metadata_path, root_hash, salt,
|
BuildVerityMetadata(
|
||||||
block_dev, signer_path, signer_key, signer_args,
|
image_size, verity_metadata_path, root_hash, salt, block_dev, signer_path,
|
||||||
verity_disable):
|
signer_key, signer_args, verity_disable)
|
||||||
return False
|
|
||||||
|
|
||||||
# build the full verified image
|
# Build the full verified image.
|
||||||
partition_size = int(prop_dict["partition_size"])
|
partition_size = int(prop_dict["partition_size"])
|
||||||
verity_size = int(prop_dict["verity_size"])
|
verity_size = int(prop_dict["verity_size"])
|
||||||
|
|
||||||
padding_size = partition_size - image_size - verity_size
|
padding_size = partition_size - image_size - verity_size
|
||||||
assert padding_size >= 0
|
assert padding_size >= 0
|
||||||
|
|
||||||
if not BuildVerifiedImage(out_file,
|
BuildVerifiedImage(
|
||||||
verity_image_path,
|
out_file, verity_image_path, verity_metadata_path, verity_fec_path,
|
||||||
verity_metadata_path,
|
padding_size, fec_supported)
|
||||||
verity_fec_path,
|
|
||||||
padding_size,
|
|
||||||
fec_supported):
|
|
||||||
return False
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def ConvertBlockMapToBaseFs(block_map_file):
|
def ConvertBlockMapToBaseFs(block_map_file):
|
||||||
base_fs_file = common.MakeTempFile(prefix="script_gen_", suffix=".base_fs")
|
base_fs_file = common.MakeTempFile(prefix="script_gen_", suffix=".base_fs")
|
||||||
convert_command = ["blk_alloc_to_base_fs", block_map_file, base_fs_file]
|
convert_command = ["blk_alloc_to_base_fs", block_map_file, base_fs_file]
|
||||||
(_, exit_code) = RunCommand(convert_command)
|
output, exit_code = RunCommand(convert_command)
|
||||||
return base_fs_file if exit_code == 0 else None
|
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):
|
def SetUpInDirAndFsConfig(origin_in, prop_dict):
|
||||||
|
@ -547,11 +546,9 @@ def CheckHeadroom(ext4fs_output, prop_dict):
|
||||||
ext4fs_output: The output string from mke2fs command.
|
ext4fs_output: The output string from mke2fs command.
|
||||||
prop_dict: The property dict.
|
prop_dict: The property dict.
|
||||||
|
|
||||||
Returns:
|
|
||||||
The check result.
|
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
AssertionError: On invalid input.
|
AssertionError: On invalid input.
|
||||||
|
BuildImageError: On check failure.
|
||||||
"""
|
"""
|
||||||
assert ext4fs_output is not None
|
assert ext4fs_output is not None
|
||||||
assert prop_dict.get('fs_type', '').startswith('ext4')
|
assert prop_dict.get('fs_type', '').startswith('ext4')
|
||||||
|
@ -569,12 +566,11 @@ def CheckHeadroom(ext4fs_output, prop_dict):
|
||||||
adjusted_blocks = total_blocks - headroom_blocks
|
adjusted_blocks = total_blocks - headroom_blocks
|
||||||
if used_blocks > adjusted_blocks:
|
if used_blocks > adjusted_blocks:
|
||||||
mount_point = prop_dict["mount_point"]
|
mount_point = prop_dict["mount_point"]
|
||||||
print("Error: Not enough room on %s (total: %d blocks, used: %d blocks, "
|
raise BuildImageError(
|
||||||
"headroom: %d blocks, available: %d blocks)" % (
|
"Error: Not enough room on {} (total: {} blocks, used: {} blocks, "
|
||||||
mount_point, total_blocks, used_blocks, headroom_blocks,
|
"headroom: {} blocks, available: {} blocks)".format(
|
||||||
adjusted_blocks))
|
mount_point, total_blocks, used_blocks, headroom_blocks,
|
||||||
return False
|
adjusted_blocks))
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def BuildImage(in_dir, prop_dict, out_file, target_out=None):
|
def BuildImage(in_dir, prop_dict, out_file, target_out=None):
|
||||||
|
@ -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
|
under system/core/libcutils) reads device specific FS config files from
|
||||||
there.
|
there.
|
||||||
|
|
||||||
Returns:
|
Raises:
|
||||||
True iff the image is built successfully.
|
BuildImageError: On build image failures.
|
||||||
"""
|
"""
|
||||||
in_dir, fs_config = SetUpInDirAndFsConfig(in_dir, prop_dict)
|
in_dir, fs_config = SetUpInDirAndFsConfig(in_dir, prop_dict)
|
||||||
|
|
||||||
|
@ -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
|
if (prop_dict.get("use_dynamic_partition_size") == "true" and
|
||||||
"partition_size" not in prop_dict):
|
"partition_size" not in prop_dict):
|
||||||
# if partition_size is not defined, use output of `du' + reserved_size
|
# If partition_size is not defined, use output of `du' + reserved_size.
|
||||||
success, size = GetDiskUsage(in_dir)
|
size = GetDiskUsage(in_dir)
|
||||||
if not success:
|
|
||||||
return False
|
|
||||||
if OPTIONS.verbose:
|
if OPTIONS.verbose:
|
||||||
print("The tree size of %s is %d MB." % (in_dir, size // BYTES_IN_MB))
|
print("The tree size of %s is %d MB." % (in_dir, size // BYTES_IN_MB))
|
||||||
size += int(prop_dict.get("partition_reserved_size", 0))
|
size += int(prop_dict.get("partition_reserved_size", 0))
|
||||||
|
@ -647,8 +641,6 @@ def BuildImage(in_dir, prop_dict, out_file, target_out=None):
|
||||||
partition_size = int(prop_dict.get("partition_size"))
|
partition_size = int(prop_dict.get("partition_size"))
|
||||||
image_size, verity_size = AdjustPartitionSizeForVerity(
|
image_size, verity_size = AdjustPartitionSizeForVerity(
|
||||||
partition_size, verity_fec_supported)
|
partition_size, verity_fec_supported)
|
||||||
if not image_size:
|
|
||||||
return False
|
|
||||||
prop_dict["image_size"] = str(image_size)
|
prop_dict["image_size"] = str(image_size)
|
||||||
prop_dict["verity_size"] = str(verity_size)
|
prop_dict["verity_size"] = str(verity_size)
|
||||||
|
|
||||||
|
@ -656,11 +648,8 @@ def BuildImage(in_dir, prop_dict, out_file, target_out=None):
|
||||||
if avb_footer_type:
|
if avb_footer_type:
|
||||||
partition_size = prop_dict["partition_size"]
|
partition_size = prop_dict["partition_size"]
|
||||||
# avb_add_hash_footer_args or avb_add_hashtree_footer_args.
|
# avb_add_hash_footer_args or avb_add_hashtree_footer_args.
|
||||||
max_image_size = AVBCalcMaxImageSize(avbtool, avb_footer_type,
|
max_image_size = AVBCalcMaxImageSize(
|
||||||
partition_size, avb_signing_args)
|
avbtool, avb_footer_type, partition_size, avb_signing_args)
|
||||||
if max_image_size <= 0:
|
|
||||||
print("AVBCalcMaxImageSize is <= 0: %d" % max_image_size)
|
|
||||||
return False
|
|
||||||
prop_dict["image_size"] = str(max_image_size)
|
prop_dict["image_size"] = str(max_image_size)
|
||||||
|
|
||||||
if fs_type.startswith("ext"):
|
if fs_type.startswith("ext"):
|
||||||
|
@ -683,8 +672,6 @@ def BuildImage(in_dir, prop_dict, out_file, target_out=None):
|
||||||
build_command.extend(["-B", prop_dict["block_list"]])
|
build_command.extend(["-B", prop_dict["block_list"]])
|
||||||
if "base_fs_file" in prop_dict:
|
if "base_fs_file" in prop_dict:
|
||||||
base_fs_file = ConvertBlockMapToBaseFs(prop_dict["base_fs_file"])
|
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(["-d", base_fs_file])
|
||||||
build_command.extend(["-L", prop_dict["mount_point"]])
|
build_command.extend(["-L", prop_dict["mount_point"]])
|
||||||
if "extfs_inode_count" in prop_dict:
|
if "extfs_inode_count" in prop_dict:
|
||||||
|
@ -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(["-T", str(prop_dict["timestamp"])])
|
||||||
build_command.extend(["-L", prop_dict["mount_point"]])
|
build_command.extend(["-L", prop_dict["mount_point"]])
|
||||||
else:
|
else:
|
||||||
print("Error: unknown filesystem type '%s'" % (fs_type))
|
raise BuildImageError(
|
||||||
return False
|
"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:
|
if exit_code != 0:
|
||||||
print("Error: '%s' failed with exit code %d:\n%s" % (
|
try:
|
||||||
build_command, exit_code, mkfs_output))
|
du = GetDiskUsage(in_dir)
|
||||||
success, du = GetDiskUsage(in_dir)
|
du_str = "{} bytes ({} MB)".format(du, du // BYTES_IN_MB)
|
||||||
du_str = ("%d bytes (%d MB)" % (du, du // BYTES_IN_MB)
|
except BuildImageError as e:
|
||||||
) if success else "unknown"
|
print(e, file=sys.stderr)
|
||||||
|
du_str = "unknown"
|
||||||
print(
|
print(
|
||||||
"Out of space? The tree size of {} is {}, with reserved space of {} "
|
"Out of space? The tree size of {} is {}, with reserved space of {} "
|
||||||
"bytes ({} MB).".format(
|
"bytes ({} MB).".format(
|
||||||
|
@ -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["image_size"]) // BYTES_IN_MB,
|
||||||
int(prop_dict["partition_size"]),
|
int(prop_dict["partition_size"]),
|
||||||
int(prop_dict["partition_size"]) // BYTES_IN_MB))
|
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.
|
# Check if there's enough headroom space available for ext4 image.
|
||||||
if "partition_headroom" in prop_dict and fs_type.startswith("ext4"):
|
if "partition_headroom" in prop_dict and fs_type.startswith("ext4"):
|
||||||
if not CheckHeadroom(mkfs_output, prop_dict):
|
CheckHeadroom(mkfs_output, prop_dict)
|
||||||
return False
|
|
||||||
|
|
||||||
if not fs_spans_partition:
|
if not fs_spans_partition:
|
||||||
mount_point = prop_dict.get("mount_point")
|
mount_point = prop_dict.get("mount_point")
|
||||||
image_size = int(prop_dict["image_size"])
|
image_size = int(prop_dict["image_size"])
|
||||||
sparse_image_size = GetSimgSize(out_file)
|
sparse_image_size = GetSimgSize(out_file)
|
||||||
if sparse_image_size > image_size:
|
if sparse_image_size > image_size:
|
||||||
print("Error: %s image size of %d is larger than partition size of "
|
raise BuildImageError(
|
||||||
"%d" % (mount_point, sparse_image_size, image_size))
|
"Error: {} image size of {} is larger than partition size of "
|
||||||
return False
|
"{}".format(mount_point, sparse_image_size, image_size))
|
||||||
if verity_supported and is_verity_partition:
|
if verity_supported and is_verity_partition:
|
||||||
ZeroPadSimg(out_file, image_size - sparse_image_size)
|
ZeroPadSimg(out_file, image_size - sparse_image_size)
|
||||||
|
|
||||||
# Create the verified image if this is to be verified.
|
# Create the verified image if this is to be verified.
|
||||||
if verity_supported and is_verity_partition:
|
if verity_supported and is_verity_partition:
|
||||||
if not MakeVerityEnabledImage(out_file, verity_fec_supported, prop_dict):
|
MakeVerityEnabledImage(out_file, verity_fec_supported, prop_dict)
|
||||||
return False
|
|
||||||
|
|
||||||
# Add AVB HASH or HASHTREE footer (metadata).
|
# Add AVB HASH or HASHTREE footer (metadata).
|
||||||
if avb_footer_type:
|
if avb_footer_type:
|
||||||
|
@ -796,30 +785,25 @@ def BuildImage(in_dir, prop_dict, out_file, target_out=None):
|
||||||
key_path = prop_dict.get("avb_key_path")
|
key_path = prop_dict.get("avb_key_path")
|
||||||
algorithm = prop_dict.get("avb_algorithm")
|
algorithm = prop_dict.get("avb_algorithm")
|
||||||
salt = prop_dict.get("avb_salt")
|
salt = prop_dict.get("avb_salt")
|
||||||
if not AVBAddFooter(out_file, avbtool, avb_footer_type,
|
AVBAddFooter(
|
||||||
partition_size, partition_name, key_path,
|
out_file, avbtool, avb_footer_type, partition_size, partition_name,
|
||||||
algorithm, salt, avb_signing_args):
|
key_path, algorithm, salt, avb_signing_args)
|
||||||
return False
|
|
||||||
|
|
||||||
if run_e2fsck and prop_dict.get("skip_fsck") != "true":
|
if run_e2fsck and prop_dict.get("skip_fsck") != "true":
|
||||||
success, unsparse_image = UnsparseImage(out_file, replace=False)
|
unsparse_image = UnsparseImage(out_file, replace=False)
|
||||||
if not success:
|
|
||||||
return False
|
|
||||||
|
|
||||||
# Run e2fsck on the inflated image file
|
# Run e2fsck on the inflated image file
|
||||||
e2fsck_command = ["e2fsck", "-f", "-n", unsparse_image]
|
e2fsck_command = ["e2fsck", "-f", "-n", unsparse_image]
|
||||||
# TODO(b/112062612): work around e2fsck failure with SANITIZE_HOST=address
|
# TODO(b/112062612): work around e2fsck failure with SANITIZE_HOST=address
|
||||||
env4e2fsck = {"ASAN_OPTIONS": "detect_odr_violation=0"}
|
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)
|
os.remove(unsparse_image)
|
||||||
|
|
||||||
if exit_code != 0:
|
if exit_code != 0:
|
||||||
print("Error: '%s' failed with exit code %d:\n%s" % (
|
raise BuildImageError(
|
||||||
e2fsck_command, exit_code, e2fsck_output))
|
"Error: '{}' failed with exit code {}:\n{}".format(
|
||||||
return False
|
e2fsck_command, exit_code, e2fsck_output))
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def ImagePropFromGlobalDict(glob_dict, mount_point):
|
def ImagePropFromGlobalDict(glob_dict, mount_point):
|
||||||
|
@ -1110,10 +1094,12 @@ def main(argv):
|
||||||
|
|
||||||
image_properties = ImagePropFromGlobalDict(glob_dict, mount_point)
|
image_properties = ImagePropFromGlobalDict(glob_dict, mount_point)
|
||||||
|
|
||||||
if not BuildImage(in_dir, image_properties, out_file, target_out):
|
try:
|
||||||
print("error: failed to build %s from %s" % (out_file, in_dir),
|
BuildImage(in_dir, image_properties, out_file, target_out)
|
||||||
|
except:
|
||||||
|
print("Error: Failed to build {} from {}".format(out_file, in_dir),
|
||||||
file=sys.stderr)
|
file=sys.stderr)
|
||||||
sys.exit(1)
|
raise
|
||||||
|
|
||||||
if prop_file_out:
|
if prop_file_out:
|
||||||
glob_dict_out = GlobalDictFromImageProp(image_properties, mount_point)
|
glob_dict_out = GlobalDictFromImageProp(image_properties, mount_point)
|
||||||
|
|
|
@ -22,8 +22,8 @@ import unittest
|
||||||
|
|
||||||
import common
|
import common
|
||||||
from build_image import (
|
from build_image import (
|
||||||
AVBCalcMinPartitionSize, BLOCK_SIZE,
|
AVBCalcMinPartitionSize, BLOCK_SIZE, BuildImageError, CheckHeadroom,
|
||||||
CheckHeadroom, RunCommand, SetUpInDirAndFsConfig)
|
RunCommand, SetUpInDirAndFsConfig)
|
||||||
|
|
||||||
|
|
||||||
class BuildImageTest(unittest.TestCase):
|
class BuildImageTest(unittest.TestCase):
|
||||||
|
@ -49,7 +49,7 @@ class BuildImageTest(unittest.TestCase):
|
||||||
'partition_headroom' : '4096000',
|
'partition_headroom' : '4096000',
|
||||||
'mount_point' : 'system',
|
'mount_point' : 'system',
|
||||||
}
|
}
|
||||||
self.assertTrue(CheckHeadroom(self.EXT4FS_OUTPUT, prop_dict))
|
CheckHeadroom(self.EXT4FS_OUTPUT, prop_dict)
|
||||||
|
|
||||||
def test_CheckHeadroom_InsufficientHeadroom(self):
|
def test_CheckHeadroom_InsufficientHeadroom(self):
|
||||||
# Required headroom: 1001 blocks.
|
# Required headroom: 1001 blocks.
|
||||||
|
@ -58,7 +58,8 @@ class BuildImageTest(unittest.TestCase):
|
||||||
'partition_headroom' : '4100096',
|
'partition_headroom' : '4100096',
|
||||||
'mount_point' : 'system',
|
'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):
|
def test_CheckHeadroom_WrongFsType(self):
|
||||||
prop_dict = {
|
prop_dict = {
|
||||||
|
@ -98,14 +99,14 @@ class BuildImageTest(unittest.TestCase):
|
||||||
'partition_headroom' : '40960',
|
'partition_headroom' : '40960',
|
||||||
'mount_point' : 'system',
|
'mount_point' : 'system',
|
||||||
}
|
}
|
||||||
self.assertTrue(CheckHeadroom(ext4fs_output, prop_dict))
|
CheckHeadroom(ext4fs_output, prop_dict)
|
||||||
|
|
||||||
prop_dict = {
|
prop_dict = {
|
||||||
'fs_type' : 'ext4',
|
'fs_type' : 'ext4',
|
||||||
'partition_headroom' : '413696',
|
'partition_headroom' : '413696',
|
||||||
'mount_point' : 'system',
|
'mount_point' : 'system',
|
||||||
}
|
}
|
||||||
self.assertFalse(CheckHeadroom(ext4fs_output, prop_dict))
|
self.assertRaises(BuildImageError, CheckHeadroom, ext4fs_output, prop_dict)
|
||||||
|
|
||||||
def test_SetUpInDirAndFsConfig_SystemRootImageTrue_NonSystem(self):
|
def test_SetUpInDirAndFsConfig_SystemRootImageTrue_NonSystem(self):
|
||||||
prop_dict = {
|
prop_dict = {
|
||||||
|
|
|
@ -139,8 +139,7 @@ class ValidateTargetFilesTest(unittest.TestCase):
|
||||||
'verity_signer_cmd' : 'verity_signer',
|
'verity_signer_cmd' : 'verity_signer',
|
||||||
'verity_size' : str(verity_size),
|
'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):
|
def test_ValidateVerifiedBootImages_systemImage(self):
|
||||||
input_tmp = common.MakeTempDir()
|
input_tmp = common.MakeTempDir()
|
||||||
|
|
Loading…
Reference in a new issue