Uses a per-partition fingerprint for building images and avb_salt.
This causes the output image files of a merged build to be identical to the image files of the input partial builds, for each images in PARTITIONS_WITH_CARE_MAP. Test: python -m unittest test_common Test: `m dist`; `unzip out/dist/target_files.zip IMAGES/\*`; `zip -d out/dist/target_files.zip IMAGES/\*` `add_img_to_target_files -a out/dist/target_files.zip`. Verify that the rebuilt images are identical to the deleted ones. Test: Build a merged target (using merge_target_files.py). Verify that the partial target-files.zip IMAGES are identical to the merged target-files.zip IMAGES for PARTITIONS_WITH_CARE_MAP images. Bug: 150405807 Change-Id: I5fdf5783c1aff9c14cf5408090389b1f65b69ca6
This commit is contained in:
@@ -338,7 +338,7 @@ def CreateImage(input_dir, info_dict, what, output_file, block_list=None):
|
|||||||
# Use repeatable ext4 FS UUID and hash_seed UUID (based on partition name and
|
# Use repeatable ext4 FS UUID and hash_seed UUID (based on partition name and
|
||||||
# build fingerprint).
|
# build fingerprint).
|
||||||
build_info = common.BuildInfo(info_dict)
|
build_info = common.BuildInfo(info_dict)
|
||||||
uuid_seed = what + "-" + build_info.fingerprint
|
uuid_seed = what + "-" + build_info.GetPartitionFingerprint(what)
|
||||||
image_props["uuid"] = str(uuid.uuid5(uuid.NAMESPACE_URL, uuid_seed))
|
image_props["uuid"] = str(uuid.uuid5(uuid.NAMESPACE_URL, uuid_seed))
|
||||||
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))
|
||||||
|
@@ -540,7 +540,6 @@ def ImagePropFromGlobalDict(glob_dict, mount_point):
|
|||||||
"verity_disable",
|
"verity_disable",
|
||||||
"avb_enable",
|
"avb_enable",
|
||||||
"avb_avbtool",
|
"avb_avbtool",
|
||||||
"avb_salt",
|
|
||||||
"use_dynamic_partition_size",
|
"use_dynamic_partition_size",
|
||||||
)
|
)
|
||||||
for p in common_props:
|
for p in common_props:
|
||||||
@@ -553,6 +552,7 @@ def ImagePropFromGlobalDict(glob_dict, mount_point):
|
|||||||
"avb_add_hashtree_footer_args")
|
"avb_add_hashtree_footer_args")
|
||||||
copy_prop("avb_system_key_path", "avb_key_path")
|
copy_prop("avb_system_key_path", "avb_key_path")
|
||||||
copy_prop("avb_system_algorithm", "avb_algorithm")
|
copy_prop("avb_system_algorithm", "avb_algorithm")
|
||||||
|
copy_prop("avb_system_salt", "avb_salt")
|
||||||
copy_prop("fs_type", "fs_type")
|
copy_prop("fs_type", "fs_type")
|
||||||
# Copy the generic system fs type first, override with specific one if
|
# Copy the generic system fs type first, override with specific one if
|
||||||
# available.
|
# available.
|
||||||
@@ -584,6 +584,7 @@ def ImagePropFromGlobalDict(glob_dict, mount_point):
|
|||||||
"avb_add_hashtree_footer_args")
|
"avb_add_hashtree_footer_args")
|
||||||
copy_prop("avb_system_other_key_path", "avb_key_path")
|
copy_prop("avb_system_other_key_path", "avb_key_path")
|
||||||
copy_prop("avb_system_other_algorithm", "avb_algorithm")
|
copy_prop("avb_system_other_algorithm", "avb_algorithm")
|
||||||
|
copy_prop("avb_system_other_salt", "avb_salt")
|
||||||
copy_prop("fs_type", "fs_type")
|
copy_prop("fs_type", "fs_type")
|
||||||
copy_prop("system_fs_type", "fs_type")
|
copy_prop("system_fs_type", "fs_type")
|
||||||
copy_prop("system_other_size", "partition_size")
|
copy_prop("system_other_size", "partition_size")
|
||||||
@@ -619,6 +620,7 @@ def ImagePropFromGlobalDict(glob_dict, mount_point):
|
|||||||
"avb_add_hashtree_footer_args")
|
"avb_add_hashtree_footer_args")
|
||||||
copy_prop("avb_vendor_key_path", "avb_key_path")
|
copy_prop("avb_vendor_key_path", "avb_key_path")
|
||||||
copy_prop("avb_vendor_algorithm", "avb_algorithm")
|
copy_prop("avb_vendor_algorithm", "avb_algorithm")
|
||||||
|
copy_prop("avb_vendor_salt", "avb_salt")
|
||||||
copy_prop("vendor_fs_type", "fs_type")
|
copy_prop("vendor_fs_type", "fs_type")
|
||||||
copy_prop("vendor_size", "partition_size")
|
copy_prop("vendor_size", "partition_size")
|
||||||
if not copy_prop("vendor_journal_size", "journal_size"):
|
if not copy_prop("vendor_journal_size", "journal_size"):
|
||||||
@@ -641,6 +643,7 @@ def ImagePropFromGlobalDict(glob_dict, mount_point):
|
|||||||
"avb_add_hashtree_footer_args")
|
"avb_add_hashtree_footer_args")
|
||||||
copy_prop("avb_product_key_path", "avb_key_path")
|
copy_prop("avb_product_key_path", "avb_key_path")
|
||||||
copy_prop("avb_product_algorithm", "avb_algorithm")
|
copy_prop("avb_product_algorithm", "avb_algorithm")
|
||||||
|
copy_prop("avb_product_salt", "avb_salt")
|
||||||
copy_prop("product_fs_type", "fs_type")
|
copy_prop("product_fs_type", "fs_type")
|
||||||
copy_prop("product_size", "partition_size")
|
copy_prop("product_size", "partition_size")
|
||||||
if not copy_prop("product_journal_size", "journal_size"):
|
if not copy_prop("product_journal_size", "journal_size"):
|
||||||
@@ -663,6 +666,7 @@ def ImagePropFromGlobalDict(glob_dict, mount_point):
|
|||||||
"avb_add_hashtree_footer_args")
|
"avb_add_hashtree_footer_args")
|
||||||
copy_prop("avb_system_ext_key_path", "avb_key_path")
|
copy_prop("avb_system_ext_key_path", "avb_key_path")
|
||||||
copy_prop("avb_system_ext_algorithm", "avb_algorithm")
|
copy_prop("avb_system_ext_algorithm", "avb_algorithm")
|
||||||
|
copy_prop("avb_system_ext_salt", "avb_salt")
|
||||||
copy_prop("system_ext_fs_type", "fs_type")
|
copy_prop("system_ext_fs_type", "fs_type")
|
||||||
copy_prop("system_ext_size", "partition_size")
|
copy_prop("system_ext_size", "partition_size")
|
||||||
if not copy_prop("system_ext_journal_size", "journal_size"):
|
if not copy_prop("system_ext_journal_size", "journal_size"):
|
||||||
@@ -687,6 +691,7 @@ def ImagePropFromGlobalDict(glob_dict, mount_point):
|
|||||||
"avb_add_hashtree_footer_args")
|
"avb_add_hashtree_footer_args")
|
||||||
copy_prop("avb_odm_key_path", "avb_key_path")
|
copy_prop("avb_odm_key_path", "avb_key_path")
|
||||||
copy_prop("avb_odm_algorithm", "avb_algorithm")
|
copy_prop("avb_odm_algorithm", "avb_algorithm")
|
||||||
|
copy_prop("avb_odm_salt", "avb_salt")
|
||||||
copy_prop("odm_fs_type", "fs_type")
|
copy_prop("odm_fs_type", "fs_type")
|
||||||
copy_prop("odm_size", "partition_size")
|
copy_prop("odm_size", "partition_size")
|
||||||
if not copy_prop("odm_journal_size", "journal_size"):
|
if not copy_prop("odm_journal_size", "journal_size"):
|
||||||
|
@@ -319,7 +319,7 @@ class BuildInfo(object):
|
|||||||
OEM-specific properties, some of them will be calculated from two info dicts.
|
OEM-specific properties, some of them will be calculated from two info dicts.
|
||||||
|
|
||||||
Users can query properties similarly as using a dict() (e.g. info['fstab']),
|
Users can query properties similarly as using a dict() (e.g. info['fstab']),
|
||||||
or to query build properties via GetBuildProp() or GetVendorBuildProp().
|
or to query build properties via GetBuildProp() or GetPartitionBuildProp().
|
||||||
|
|
||||||
Attributes:
|
Attributes:
|
||||||
info_dict: The build-time info dict.
|
info_dict: The build-time info dict.
|
||||||
@@ -362,16 +362,31 @@ class BuildInfo(object):
|
|||||||
if self._oem_props:
|
if self._oem_props:
|
||||||
assert oem_dicts, "OEM source required for this build"
|
assert oem_dicts, "OEM source required for this build"
|
||||||
|
|
||||||
|
def check_fingerprint(fingerprint):
|
||||||
|
if (" " in fingerprint or any(ord(ch) > 127 for ch in fingerprint)):
|
||||||
|
raise ValueError(
|
||||||
|
'Invalid build fingerprint: "{}". See the requirement in Android CDD '
|
||||||
|
"3.2.2. Build Parameters.".format(fingerprint))
|
||||||
|
|
||||||
|
|
||||||
|
self._partition_fingerprints = {}
|
||||||
|
for partition in PARTITIONS_WITH_CARE_MAP:
|
||||||
|
try:
|
||||||
|
fingerprint = self.CalculatePartitionFingerprint(partition)
|
||||||
|
check_fingerprint(fingerprint)
|
||||||
|
self._partition_fingerprints[partition] = fingerprint
|
||||||
|
except ExternalError:
|
||||||
|
continue
|
||||||
|
if "system" in self._partition_fingerprints:
|
||||||
|
# system_other is not included in PARTITIONS_WITH_CARE_MAP, but does
|
||||||
|
# need a fingerprint when creating the image.
|
||||||
|
self._partition_fingerprints[
|
||||||
|
"system_other"] = self._partition_fingerprints["system"]
|
||||||
|
|
||||||
# These two should be computed only after setting self._oem_props.
|
# These two should be computed only after setting self._oem_props.
|
||||||
self._device = self.GetOemProperty("ro.product.device")
|
self._device = self.GetOemProperty("ro.product.device")
|
||||||
self._fingerprint = self.CalculateFingerprint()
|
self._fingerprint = self.CalculateFingerprint()
|
||||||
|
check_fingerprint(self._fingerprint)
|
||||||
# Sanity check the build fingerprint.
|
|
||||||
if (' ' in self._fingerprint or
|
|
||||||
any(ord(ch) > 127 for ch in self._fingerprint)):
|
|
||||||
raise ValueError(
|
|
||||||
'Invalid build fingerprint: "{}". See the requirement in Android CDD '
|
|
||||||
'3.2.2. Build Parameters.'.format(self._fingerprint))
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_ab(self):
|
def is_ab(self):
|
||||||
@@ -385,28 +400,6 @@ class BuildInfo(object):
|
|||||||
def fingerprint(self):
|
def fingerprint(self):
|
||||||
return self._fingerprint
|
return self._fingerprint
|
||||||
|
|
||||||
@property
|
|
||||||
def vendor_fingerprint(self):
|
|
||||||
return self._fingerprint_of("vendor")
|
|
||||||
|
|
||||||
@property
|
|
||||||
def product_fingerprint(self):
|
|
||||||
return self._fingerprint_of("product")
|
|
||||||
|
|
||||||
@property
|
|
||||||
def odm_fingerprint(self):
|
|
||||||
return self._fingerprint_of("odm")
|
|
||||||
|
|
||||||
def _fingerprint_of(self, partition):
|
|
||||||
if partition + ".build.prop" not in self.info_dict:
|
|
||||||
return None
|
|
||||||
build_prop = self.info_dict[partition + ".build.prop"]
|
|
||||||
if "ro." + partition + ".build.fingerprint" in build_prop:
|
|
||||||
return build_prop["ro." + partition + ".build.fingerprint"]
|
|
||||||
if "ro." + partition + ".build.thumbprint" in build_prop:
|
|
||||||
return build_prop["ro." + partition + ".build.thumbprint"]
|
|
||||||
return None
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def oem_props(self):
|
def oem_props(self):
|
||||||
return self._oem_props
|
return self._oem_props
|
||||||
@@ -423,8 +416,22 @@ class BuildInfo(object):
|
|||||||
def items(self):
|
def items(self):
|
||||||
return self.info_dict.items()
|
return self.info_dict.items()
|
||||||
|
|
||||||
|
def GetPartitionBuildProp(self, prop, partition):
|
||||||
|
"""Returns the inquired build property for the provided partition."""
|
||||||
|
# If provided a partition for this property, only look within that
|
||||||
|
# partition's build.prop.
|
||||||
|
if prop in BuildInfo._RO_PRODUCT_RESOLVE_PROPS:
|
||||||
|
prop = prop.replace("ro.product", "ro.product.{}".format(partition))
|
||||||
|
else:
|
||||||
|
prop = prop.replace("ro.", "ro.{}.".format(partition))
|
||||||
|
try:
|
||||||
|
return self.info_dict.get("{}.build.prop".format(partition), {})[prop]
|
||||||
|
except KeyError:
|
||||||
|
raise ExternalError("couldn't find %s in %s.build.prop" %
|
||||||
|
(prop, partition))
|
||||||
|
|
||||||
def GetBuildProp(self, prop):
|
def GetBuildProp(self, prop):
|
||||||
"""Returns the inquired build property."""
|
"""Returns the inquired build property from the standard build.prop file."""
|
||||||
if prop in BuildInfo._RO_PRODUCT_RESOLVE_PROPS:
|
if prop in BuildInfo._RO_PRODUCT_RESOLVE_PROPS:
|
||||||
return self._ResolveRoProductBuildProp(prop)
|
return self._ResolveRoProductBuildProp(prop)
|
||||||
|
|
||||||
@@ -462,19 +469,28 @@ class BuildInfo(object):
|
|||||||
|
|
||||||
raise ExternalError("couldn't resolve {}".format(prop))
|
raise ExternalError("couldn't resolve {}".format(prop))
|
||||||
|
|
||||||
def GetVendorBuildProp(self, prop):
|
|
||||||
"""Returns the inquired vendor build property."""
|
|
||||||
try:
|
|
||||||
return self.info_dict.get("vendor.build.prop", {})[prop]
|
|
||||||
except KeyError:
|
|
||||||
raise ExternalError(
|
|
||||||
"couldn't find %s in vendor.build.prop" % (prop,))
|
|
||||||
|
|
||||||
def GetOemProperty(self, key):
|
def GetOemProperty(self, key):
|
||||||
if self.oem_props is not None and key in self.oem_props:
|
if self.oem_props is not None and key in self.oem_props:
|
||||||
return self.oem_dicts[0][key]
|
return self.oem_dicts[0][key]
|
||||||
return self.GetBuildProp(key)
|
return self.GetBuildProp(key)
|
||||||
|
|
||||||
|
def GetPartitionFingerprint(self, partition):
|
||||||
|
return self._partition_fingerprints.get(partition, None)
|
||||||
|
|
||||||
|
def CalculatePartitionFingerprint(self, partition):
|
||||||
|
try:
|
||||||
|
return self.GetPartitionBuildProp("ro.build.fingerprint", partition)
|
||||||
|
except ExternalError:
|
||||||
|
return "{}/{}/{}:{}/{}/{}:{}/{}".format(
|
||||||
|
self.GetPartitionBuildProp("ro.product.brand", partition),
|
||||||
|
self.GetPartitionBuildProp("ro.product.name", partition),
|
||||||
|
self.GetPartitionBuildProp("ro.product.device", partition),
|
||||||
|
self.GetPartitionBuildProp("ro.build.version.release", partition),
|
||||||
|
self.GetPartitionBuildProp("ro.build.id", partition),
|
||||||
|
self.GetPartitionBuildProp("ro.build.version.incremental", partition),
|
||||||
|
self.GetPartitionBuildProp("ro.build.type", partition),
|
||||||
|
self.GetPartitionBuildProp("ro.build.tags", partition))
|
||||||
|
|
||||||
def CalculateFingerprint(self):
|
def CalculateFingerprint(self):
|
||||||
if self.oem_props is None:
|
if self.oem_props is None:
|
||||||
try:
|
try:
|
||||||
@@ -644,7 +660,10 @@ def LoadInfoDict(input_file, repacking=False):
|
|||||||
# hash / hashtree footers.
|
# hash / hashtree footers.
|
||||||
if d.get("avb_enable") == "true":
|
if d.get("avb_enable") == "true":
|
||||||
build_info = BuildInfo(d)
|
build_info = BuildInfo(d)
|
||||||
d["avb_salt"] = sha256(build_info.fingerprint).hexdigest()
|
for partition in PARTITIONS_WITH_CARE_MAP:
|
||||||
|
fingerprint = build_info.GetPartitionFingerprint(partition)
|
||||||
|
if fingerprint:
|
||||||
|
d["avb_{}_salt".format(partition)] = sha256(fingerprint).hexdigest()
|
||||||
|
|
||||||
return d
|
return d
|
||||||
|
|
||||||
|
@@ -53,8 +53,26 @@ class BuildInfoTest(test_utils.ReleaseToolsTestCase):
|
|||||||
'ro.build.fingerprint' : 'build-fingerprint',
|
'ro.build.fingerprint' : 'build-fingerprint',
|
||||||
'ro.build.foo' : 'build-foo',
|
'ro.build.foo' : 'build-foo',
|
||||||
},
|
},
|
||||||
|
'system.build.prop' : {
|
||||||
|
'ro.product.system.brand' : 'product-brand',
|
||||||
|
'ro.product.system.name' : 'product-name',
|
||||||
|
'ro.product.system.device' : 'product-device',
|
||||||
|
'ro.system.build.version.release' : 'version-release',
|
||||||
|
'ro.system.build.id' : 'build-id',
|
||||||
|
'ro.system.build.version.incremental' : 'version-incremental',
|
||||||
|
'ro.system.build.type' : 'build-type',
|
||||||
|
'ro.system.build.tags' : 'build-tags',
|
||||||
|
'ro.system.build.foo' : 'build-foo',
|
||||||
|
},
|
||||||
'vendor.build.prop' : {
|
'vendor.build.prop' : {
|
||||||
'ro.vendor.build.fingerprint' : 'vendor-build-fingerprint',
|
'ro.product.vendor.brand' : 'vendor-product-brand',
|
||||||
|
'ro.product.vendor.name' : 'vendor-product-name',
|
||||||
|
'ro.product.vendor.device' : 'vendor-product-device',
|
||||||
|
'ro.vendor.build.version.release' : 'vendor-version-release',
|
||||||
|
'ro.vendor.build.id' : 'vendor-build-id',
|
||||||
|
'ro.vendor.build.version.incremental' : 'vendor-version-incremental',
|
||||||
|
'ro.vendor.build.type' : 'vendor-build-type',
|
||||||
|
'ro.vendor.build.tags' : 'vendor-build-tags',
|
||||||
},
|
},
|
||||||
'property1' : 'value1',
|
'property1' : 'value1',
|
||||||
'property2' : 4096,
|
'property2' : 4096,
|
||||||
@@ -186,39 +204,27 @@ class BuildInfoTest(test_utils.ReleaseToolsTestCase):
|
|||||||
self.assertRaises(common.ExternalError, target_info.GetBuildProp,
|
self.assertRaises(common.ExternalError, target_info.GetBuildProp,
|
||||||
'ro.build.nonexistent')
|
'ro.build.nonexistent')
|
||||||
|
|
||||||
def test_GetVendorBuildProp(self):
|
def test_GetPartitionFingerprint(self):
|
||||||
target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
|
target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
|
||||||
self.assertEqual('vendor-build-fingerprint',
|
self.assertEqual(
|
||||||
target_info.GetVendorBuildProp(
|
target_info.GetPartitionFingerprint('vendor'),
|
||||||
'ro.vendor.build.fingerprint'))
|
'vendor-product-brand/vendor-product-name/vendor-product-device'
|
||||||
self.assertRaises(common.ExternalError, target_info.GetVendorBuildProp,
|
':vendor-version-release/vendor-build-id/vendor-version-incremental'
|
||||||
'ro.build.nonexistent')
|
':vendor-build-type/vendor-build-tags')
|
||||||
|
|
||||||
def test_GetVendorBuildProp_with_oem_props(self):
|
def test_GetPartitionFingerprint_system_other_uses_system(self):
|
||||||
target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
|
|
||||||
self.TEST_OEM_DICTS)
|
|
||||||
self.assertEqual('vendor-build-fingerprint',
|
|
||||||
target_info.GetVendorBuildProp(
|
|
||||||
'ro.vendor.build.fingerprint'))
|
|
||||||
self.assertRaises(common.ExternalError, target_info.GetVendorBuildProp,
|
|
||||||
'ro.build.nonexistent')
|
|
||||||
|
|
||||||
def test_vendor_fingerprint(self):
|
|
||||||
target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
|
target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
|
||||||
self.assertEqual('vendor-build-fingerprint',
|
self.assertEqual(
|
||||||
target_info.vendor_fingerprint)
|
target_info.GetPartitionFingerprint('system_other'),
|
||||||
|
target_info.GetPartitionFingerprint('system'))
|
||||||
|
|
||||||
def test_vendor_fingerprint_blacklisted(self):
|
def test_GetPartitionFingerprint_uses_fingerprint_prop_if_available(self):
|
||||||
target_info_dict = copy.deepcopy(self.TEST_INFO_DICT_USES_OEM_PROPS)
|
info_dict = copy.deepcopy(self.TEST_INFO_DICT)
|
||||||
del target_info_dict['vendor.build.prop']['ro.vendor.build.fingerprint']
|
info_dict['vendor.build.prop']['ro.vendor.build.fingerprint'] = 'vendor:fingerprint'
|
||||||
target_info = common.BuildInfo(target_info_dict, self.TEST_OEM_DICTS)
|
target_info = common.BuildInfo(info_dict, None)
|
||||||
self.assertIsNone(target_info.vendor_fingerprint)
|
self.assertEqual(
|
||||||
|
target_info.GetPartitionFingerprint('vendor'),
|
||||||
def test_vendor_fingerprint_without_vendor_build_prop(self):
|
'vendor:fingerprint')
|
||||||
target_info_dict = copy.deepcopy(self.TEST_INFO_DICT_USES_OEM_PROPS)
|
|
||||||
del target_info_dict['vendor.build.prop']
|
|
||||||
target_info = common.BuildInfo(target_info_dict, self.TEST_OEM_DICTS)
|
|
||||||
self.assertIsNone(target_info.vendor_fingerprint)
|
|
||||||
|
|
||||||
def test_WriteMountOemScript(self):
|
def test_WriteMountOemScript(self):
|
||||||
target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
|
target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
|
||||||
|
Reference in New Issue
Block a user