summaryrefslogtreecommitdiffstats
path: root/tools/releasetools/build_image.py
diff options
context:
space:
mode:
Diffstat (limited to 'tools/releasetools/build_image.py')
-rwxr-xr-xtools/releasetools/build_image.py234
1 files changed, 223 insertions, 11 deletions
diff --git a/tools/releasetools/build_image.py b/tools/releasetools/build_image.py
index f8f2ada..712e0cd 100755
--- a/tools/releasetools/build_image.py
+++ b/tools/releasetools/build_image.py
@@ -24,6 +24,13 @@ import os
import os.path
import subprocess
import sys
+import commands
+import shutil
+import tempfile
+
+import simg_map
+
+FIXED_SALT = "aee087a5be3b982978c923f566a94613496b417f2af592639bc80d141e34dfe7"
def RunCommand(cmd):
""" Echo and run the given command
@@ -38,13 +45,182 @@ def RunCommand(cmd):
p.communicate()
return p.returncode
-def BuildImage(in_dir, prop_dict, out_file):
+def GetVerityTreeSize(partition_size):
+ cmd = "build_verity_tree -s %d"
+ cmd %= partition_size
+ status, output = commands.getstatusoutput(cmd)
+ if status:
+ print output
+ return False, 0
+ return True, int(output)
+
+def GetVerityMetadataSize(partition_size):
+ cmd = "system/extras/verity/build_verity_metadata.py -s %d"
+ cmd %= partition_size
+ status, output = commands.getstatusoutput(cmd)
+ if status:
+ print output
+ return False, 0
+ return True, int(output)
+
+def AdjustPartitionSizeForVerity(partition_size):
+ """Modifies the provided partition size to account for the verity metadata.
+
+ This information is used to size the created image appropriately.
+ Args:
+ partition_size: the size of the partition to be verified.
+ Returns:
+ The size of the partition adjusted for verity metadata.
+ """
+ success, verity_tree_size = GetVerityTreeSize(partition_size)
+ if not success:
+ return 0;
+ success, verity_metadata_size = GetVerityMetadataSize(partition_size)
+ if not success:
+ return 0
+ return partition_size - verity_tree_size - verity_metadata_size
+
+def BuildVerityTree(sparse_image_path, verity_image_path, prop_dict):
+ cmd = ("build_verity_tree -A %s %s %s" % (FIXED_SALT, sparse_image_path, verity_image_path))
+ print cmd
+ status, output = commands.getstatusoutput(cmd)
+ if status:
+ print "Could not build verity tree! Error: %s" % output
+ return False
+ 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,
+ block_device, signer_path, key):
+ cmd = ("system/extras/verity/build_verity_metadata.py %s %s %s %s %s %s %s" %
+ (image_size,
+ verity_metadata_path,
+ root_hash,
+ salt,
+ block_device,
+ signer_path,
+ key))
+ print cmd
+ status, output = commands.getstatusoutput(cmd)
+ if status:
+ print "Could not build verity metadata! Error: %s" % output
+ return False
+ return True
+
+def Append2Simg(sparse_image_path, unsparse_image_path, error_message):
+ """Appends the unsparse image to the given sparse image.
+
+ 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.
+ """
+ cmd = "append2simg %s %s"
+ cmd %= (sparse_image_path, unsparse_image_path)
+ print cmd
+ status, output = commands.getstatusoutput(cmd)
+ if status:
+ print "%s: %s" % (error_message, output)
+ return False
+ return True
+
+def BuildVerifiedImage(data_image_path, verity_image_path, verity_metadata_path):
+ if not Append2Simg(data_image_path, verity_metadata_path, "Could not append verity metadata!"):
+ return False
+ if not Append2Simg(data_image_path, verity_image_path, "Could not append verity tree!"):
+ return False
+ return True
+
+def UnsparseImage(sparse_image_path, replace=True):
+ img_dir = os.path.dirname(sparse_image_path)
+ unsparse_image_path = "unsparse_" + os.path.basename(sparse_image_path)
+ unsparse_image_path = os.path.join(img_dir, unsparse_image_path)
+ if os.path.exists(unsparse_image_path):
+ if replace:
+ os.unlink(unsparse_image_path)
+ else:
+ return True, unsparse_image_path
+ inflate_command = ["simg2img", sparse_image_path, unsparse_image_path]
+ exit_code = RunCommand(inflate_command)
+ if exit_code != 0:
+ os.remove(unsparse_image_path)
+ return False, None
+ return True, unsparse_image_path
+
+def MappedUnsparseImage(sparse_image_path, unsparse_image_path,
+ map_path, mapped_unsparse_image_path):
+ if simg_map.ComputeMap(sparse_image_path, unsparse_image_path,
+ map_path, mapped_unsparse_image_path):
+ return False
+ return True
+
+def MakeVerityEnabledImage(out_file, prop_dict):
+ """Creates an image that is verifiable using dm-verity.
+
+ Args:
+ 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.
+ """
+ # get properties
+ image_size = prop_dict["partition_size"]
+ block_dev = prop_dict["verity_block_device"]
+ signer_key = prop_dict["verity_key"]
+ signer_path = prop_dict["verity_signer_cmd"]
+
+ # make a tempdir
+ tempdir_name = tempfile.mkdtemp(suffix="_verity_images")
+
+ # 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")
+
+ # build the verity tree and get the root hash and salt
+ if not BuildVerityTree(out_file, verity_image_path, prop_dict):
+ shutil.rmtree(tempdir_name, ignore_errors=True)
+ return False
+
+ # build the metadata blocks
+ root_hash = prop_dict["verity_root_hash"]
+ salt = prop_dict["verity_salt"]
+ if not BuildVerityMetadata(image_size,
+ verity_metadata_path,
+ root_hash,
+ salt,
+ block_dev,
+ signer_path,
+ signer_key):
+ shutil.rmtree(tempdir_name, ignore_errors=True)
+ return False
+
+ # build the full verified image
+ if not BuildVerifiedImage(out_file,
+ verity_image_path,
+ verity_metadata_path):
+ shutil.rmtree(tempdir_name, ignore_errors=True)
+ return False
+
+ shutil.rmtree(tempdir_name, ignore_errors=True)
+ return True
+
+def BuildImage(in_dir, prop_dict, out_file,
+ fs_config=None,
+ fc_config=None):
"""Build an image to out_file from in_dir with property prop_dict.
Args:
in_dir: path of input directory.
prop_dict: property dictionary.
out_file: path of the output image file.
+ fs_config: path to the fs_config file (typically
+ META/filesystem_config.txt). If None then the configuration in
+ the local client will be used.
+ fc_config: path to the SELinux file_contexts file. If None then
+ the value from prop_dict['selinux_fc'] will be used.
Returns:
True iff the image is built successfully.
@@ -52,6 +228,18 @@ def BuildImage(in_dir, prop_dict, out_file):
build_command = []
fs_type = prop_dict.get("fs_type", "")
run_fsck = False
+
+ is_verity_partition = "verity_block_device" in prop_dict
+ verity_supported = prop_dict.get("verity") == "true"
+ # 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 = AdjustPartitionSizeForVerity(partition_size)
+ if not adjusted_size:
+ return False
+ prop_dict["partition_size"] = str(adjusted_size)
+ prop_dict["original_partition_size"] = str(partition_size)
+
if fs_type.startswith("ext"):
build_command = ["mkuserimg.sh"]
if "extfs_sparse_flag" in prop_dict:
@@ -59,10 +247,18 @@ def BuildImage(in_dir, prop_dict, out_file):
run_fsck = True
build_command.extend([in_dir, out_file, fs_type,
prop_dict["mount_point"]])
- if "partition_size" in prop_dict:
- build_command.append(prop_dict["partition_size"])
- if "selinux_fc" in prop_dict:
+ build_command.append(prop_dict["partition_size"])
+ if "timestamp" in prop_dict:
+ build_command.extend(["-T", str(prop_dict["timestamp"])])
+ if fs_config is not None:
+ build_command.extend(["-C", fs_config])
+ if fc_config is not None:
+ build_command.append(fc_config)
+ elif "selinux_fc" in prop_dict:
build_command.append(prop_dict["selinux_fc"])
+ elif fs_type.startswith("f2fs"):
+ build_command = ["mkf2fsuserimg.sh"]
+ build_command.extend([out_file, prop_dict["partition_size"]])
else:
build_command = ["mkyaffs2image", "-f"]
if prop_dict.get("mkyaffs2_extra_flags", None):
@@ -77,14 +273,14 @@ def BuildImage(in_dir, prop_dict, out_file):
if exit_code != 0:
return False
+ # create the verified image if this is to be verified
+ if verity_supported and is_verity_partition:
+ if not MakeVerityEnabledImage(out_file, prop_dict):
+ return False
+
if run_fsck and prop_dict.get("skip_fsck") != "true":
- # Inflate the sparse image
- unsparse_image = os.path.join(
- os.path.dirname(out_file), "unsparse_" + os.path.basename(out_file))
- inflate_command = ["simg2img", out_file, unsparse_image]
- exit_code = RunCommand(inflate_command)
- if exit_code != 0:
- os.remove(unsparse_image)
+ success, unsparse_image = UnsparseImage(out_file, replace=False)
+ if not success:
return False
# Run e2fsck on the inflated image file
@@ -104,6 +300,10 @@ def ImagePropFromGlobalDict(glob_dict, mount_point):
mount_point: such as "system", "data" etc.
"""
d = {}
+ if "build.prop" in glob_dict:
+ bp = glob_dict["build.prop"]
+ if "ro.build.date.utc" in bp:
+ d["timestamp"] = bp["ro.build.date.utc"]
def copy_prop(src_p, dest_p):
if src_p in glob_dict:
@@ -114,6 +314,9 @@ def ImagePropFromGlobalDict(glob_dict, mount_point):
"mkyaffs2_extra_flags",
"selinux_fc",
"skip_fsck",
+ "verity",
+ "verity_key",
+ "verity_signer_cmd"
)
for p in common_props:
copy_prop(p, p)
@@ -122,8 +325,11 @@ def ImagePropFromGlobalDict(glob_dict, mount_point):
if mount_point == "system":
copy_prop("fs_type", "fs_type")
copy_prop("system_size", "partition_size")
+ copy_prop("system_verity_block_device", "verity_block_device")
elif mount_point == "data":
+ # Copy the generic fs type first, override with specific one if available.
copy_prop("fs_type", "fs_type")
+ copy_prop("userdata_fs_type", "fs_type")
copy_prop("userdata_size", "partition_size")
elif mount_point == "cache":
copy_prop("cache_fs_type", "fs_type")
@@ -131,6 +337,10 @@ def ImagePropFromGlobalDict(glob_dict, mount_point):
elif mount_point == "vendor":
copy_prop("vendor_fs_type", "fs_type")
copy_prop("vendor_size", "partition_size")
+ copy_prop("vendor_verity_block_device", "verity_block_device")
+ elif mount_point == "oem":
+ copy_prop("fs_type", "fs_type")
+ copy_prop("oem_size", "partition_size")
return d
@@ -169,6 +379,8 @@ def main(argv):
mount_point = "cache"
elif image_filename == "vendor.img":
mount_point = "vendor"
+ elif image_filename == "oem.img":
+ mount_point = "oem"
else:
print >> sys.stderr, "error: unknown image file name ", image_filename
exit(1)