Regenerate odm or vendor using combined sepolicy if --rebuild-sepolicy.

This allows merged devices to boot using a precompiled_sepolicy built
from merged sources, rather than recompiling this sepolicy at boot
time every boot.

Bug: 178727214
Test: Merge an R+S build using --rebuild-sepolicy and --vendor-otatools.
      Observe odm.img is rebuilt by the vendor otatools.zip
        when merging.
      Observe device boots using ODM's precompiled_sepolicy file.
Test: Same as above, for S+S.
Test: Merge an S+S build using --rebuild-sepolicy and *not*
        --vendor-otatools.
      Observe odm.img is rebuilt without using a separate otatools.zip.
      Observe device boots using ODM's precompiled_sepolicy file.
Change-Id: I9595b8a3296d6deec21db8f0c9bc5b7ec4debd57
This commit is contained in:
Daniel Norman
2021-06-25 17:18:25 -07:00
parent 0427fb4a97
commit 571e182e9c
2 changed files with 122 additions and 14 deletions

View File

@@ -78,6 +78,14 @@ Usage: merge_target_files [args]
If provided, duplicate APK/APEX keys are ignored and the value from the If provided, duplicate APK/APEX keys are ignored and the value from the
framework is used. framework is used.
--rebuild-sepolicy
If provided, rebuilds odm.img or vendor.img to include merged sepolicy
files. If odm is present then odm is preferred.
--vendor-otatools otatools.zip
If provided, use this otatools.zip when recompiling the odm or vendor
image to include sepolicy.
--keep-tmp --keep-tmp
Keep tempoary files for debugging purposes. Keep tempoary files for debugging purposes.
""" """
@@ -129,6 +137,8 @@ OPTIONS.output_super_empty = None
OPTIONS.rebuild_recovery = False OPTIONS.rebuild_recovery = False
# TODO(b/150582573): Remove this option. # TODO(b/150582573): Remove this option.
OPTIONS.allow_duplicate_apkapex_keys = False OPTIONS.allow_duplicate_apkapex_keys = False
OPTIONS.vendor_otatools = None
OPTIONS.rebuild_sepolicy = False
OPTIONS.keep_tmp = False OPTIONS.keep_tmp = False
# In an item list (framework or vendor), we may see entries that select whole # In an item list (framework or vendor), we may see entries that select whole
@@ -666,7 +676,7 @@ def copy_file_contexts(framework_target_files_dir, vendor_target_files_dir,
os.path.join(output_target_files_dir, 'META', 'vendor_file_contexts.bin')) os.path.join(output_target_files_dir, 'META', 'vendor_file_contexts.bin'))
def compile_split_sepolicy(product_out, partition_map, output_policy): def compile_split_sepolicy(product_out, partition_map):
"""Uses secilc to compile a split sepolicy file. """Uses secilc to compile a split sepolicy file.
Depends on various */etc/selinux/* and */etc/vintf/* files within partitions. Depends on various */etc/selinux/* and */etc/vintf/* files within partitions.
@@ -674,7 +684,6 @@ def compile_split_sepolicy(product_out, partition_map, output_policy):
Args: Args:
product_out: PRODUCT_OUT directory, containing partition directories. product_out: PRODUCT_OUT directory, containing partition directories.
partition_map: A map of partition name -> relative path within product_out. partition_map: A map of partition name -> relative path within product_out.
output_policy: The name of the output policy created by secilc.
Returns: Returns:
A command list that can be executed to create the compiled sepolicy. A command list that can be executed to create the compiled sepolicy.
@@ -709,7 +718,7 @@ def compile_split_sepolicy(product_out, partition_map, output_policy):
# Use the same flags and arguments as selinux.cpp OpenSplitPolicy(). # Use the same flags and arguments as selinux.cpp OpenSplitPolicy().
cmd = ['secilc', '-m', '-M', 'true', '-G', '-N'] cmd = ['secilc', '-m', '-M', 'true', '-G', '-N']
cmd.extend(['-c', kernel_sepolicy_version]) cmd.extend(['-c', kernel_sepolicy_version])
cmd.extend(['-o', output_policy]) cmd.extend(['-o', os.path.join(product_out, 'META/combined_sepolicy')])
cmd.extend(['-f', '/dev/null']) cmd.extend(['-f', '/dev/null'])
required_policy_files = ( required_policy_files = (
@@ -747,7 +756,8 @@ def validate_merged_apex_info(output_target_files_dir, partitions):
Depends on the <partition>/apex/* APEX files within partitions. Depends on the <partition>/apex/* APEX files within partitions.
Args: Args:
output_target_files_dir: Output directory containing merged partition directories. output_target_files_dir: Output directory containing merged partition
directories.
partitions: A list of all the partitions in the output directory. partitions: A list of all the partitions in the output directory.
Raises: Raises:
@@ -965,6 +975,92 @@ def generate_images(target_files_dir, rebuild_recovery):
add_img_to_target_files.main(add_img_args) add_img_to_target_files.main(add_img_args)
def rebuild_image_with_sepolicy(target_files_dir,
vendor_otatools=None,
vendor_target_files=None):
"""Rebuilds odm.img or vendor.img to include merged sepolicy files.
If odm is present then odm is preferred -- otherwise vendor is used.
Args:
target_files_dir: Path to the extracted merged target-files package.
vendor_otatools: If not None, path to an otatools.zip from the vendor build
that is used when recompiling the image.
vendor_target_files: Expected if vendor_otatools is not None. Path to the
vendor target-files zip.
"""
partition = 'vendor'
if os.path.exists(os.path.join(target_files_dir, 'ODM')) or os.path.exists(
os.path.join(target_files_dir, 'IMAGES/odm.img')):
partition = 'odm'
partition_img = '{}.img'.format(partition)
logger.info('Recompiling %s using the merged sepolicy files.', partition_img)
# Copy the combined SEPolicy file and framework hashes to the image that is
# being rebuilt.
def copy_selinux_file(input_path, output_filename):
shutil.copy(
os.path.join(target_files_dir, input_path),
os.path.join(target_files_dir, partition.upper(), 'etc/selinux',
output_filename))
copy_selinux_file('META/combined_sepolicy', 'precompiled_sepolicy')
copy_selinux_file('SYSTEM/etc/selinux/plat_sepolicy_and_mapping.sha256',
'precompiled_sepolicy.plat_sepolicy_and_mapping.sha256')
copy_selinux_file(
'SYSTEM_EXT/etc/selinux/system_ext_sepolicy_and_mapping.sha256',
'precompiled_sepolicy.system_ext_sepolicy_and_mapping.sha256')
copy_selinux_file('PRODUCT/etc/selinux/product_sepolicy_and_mapping.sha256',
'precompiled_sepolicy.product_sepolicy_and_mapping.sha256')
if not vendor_otatools:
# Remove the partition from the merged target-files archive. It will be
# rebuilt later automatically by generate_images().
os.remove(os.path.join(target_files_dir, 'IMAGES', partition_img))
else:
# TODO(b/192253131): Remove the need for vendor_otatools by fixing
# backwards-compatibility issues when compiling images on R from S+.
if not vendor_target_files:
raise ValueError(
'Expected vendor_target_files if vendor_otatools is not None.')
logger.info(
'%s recompilation will be performed using the vendor otatools.zip',
partition_img)
# Unzip the vendor build's otatools.zip and target-files archive.
vendor_otatools_dir = common.MakeTempDir(
prefix='merge_target_files_vendor_otatools_')
vendor_target_files_dir = common.MakeTempDir(
prefix='merge_target_files_vendor_target_files_')
common.UnzipToDir(vendor_otatools, vendor_otatools_dir)
common.UnzipToDir(vendor_target_files, vendor_target_files_dir)
# Copy the partition contents from the merged target-files archive to the
# vendor target-files archive.
shutil.rmtree(os.path.join(vendor_target_files_dir, partition.upper()))
shutil.copytree(
os.path.join(target_files_dir, partition.upper()),
os.path.join(vendor_target_files_dir, partition.upper()))
# Delete then rebuild the partition.
os.remove(os.path.join(vendor_target_files_dir, 'IMAGES', partition_img))
rebuild_partition_command = [
os.path.join(vendor_otatools_dir, 'bin', 'add_img_to_target_files'),
'--verbose',
'--add_missing',
vendor_target_files_dir,
]
logger.info('Recompiling %s: %s', partition_img,
' '.join(rebuild_partition_command))
common.RunAndCheckOutput(rebuild_partition_command, verbose=True)
# Move the newly-created image to the merged target files dir.
shutil.move(
os.path.join(vendor_target_files_dir, 'IMAGES', partition_img),
os.path.join(target_files_dir, 'IMAGES', partition_img))
def generate_super_empty_image(target_dir, output_super_empty): def generate_super_empty_image(target_dir, output_super_empty):
"""Generates super_empty image from target package. """Generates super_empty image from target package.
@@ -1049,7 +1145,8 @@ def merge_target_files(temp_dir, framework_target_files, framework_item_list,
framework_misc_info_keys, vendor_target_files, framework_misc_info_keys, vendor_target_files,
vendor_item_list, output_target_files, output_dir, vendor_item_list, output_target_files, output_dir,
output_item_list, output_ota, output_img, output_item_list, output_ota, output_img,
output_super_empty, rebuild_recovery): output_super_empty, rebuild_recovery, vendor_otatools,
rebuild_sepolicy):
"""Merges two target files packages together. """Merges two target files packages together.
This function takes framework and vendor target files packages as input, This function takes framework and vendor target files packages as input,
@@ -1085,6 +1182,9 @@ def merge_target_files(temp_dir, framework_target_files, framework_item_list,
merged target files package and saves it at this path. merged target files package and saves it at this path.
rebuild_recovery: If true, rebuild the recovery patch used by non-A/B rebuild_recovery: If true, rebuild the recovery patch used by non-A/B
devices and write it to the system image. devices and write it to the system image.
vendor_otatools: Path to an otatools zip used for recompiling vendor images.
rebuild_sepolicy: If true, rebuild odm.img (if target uses ODM) or
vendor.img using a merged precompiled_sepolicy file.
""" """
logger.info('starting: merge framework %s and vendor %s into output %s', logger.info('starting: merge framework %s and vendor %s into output %s',
@@ -1137,14 +1237,14 @@ def merge_target_files(temp_dir, framework_target_files, framework_item_list,
partition_map=filtered_partitions) partition_map=filtered_partitions)
# Check that the split sepolicy from the multiple builds can compile. # Check that the split sepolicy from the multiple builds can compile.
split_sepolicy_cmd = compile_split_sepolicy( split_sepolicy_cmd = compile_split_sepolicy(output_target_files_temp_dir,
product_out=output_target_files_temp_dir, filtered_partitions)
partition_map=filtered_partitions,
output_policy=os.path.join(output_target_files_temp_dir,
'META/combined.policy'))
logger.info('Compiling split sepolicy: %s', ' '.join(split_sepolicy_cmd)) logger.info('Compiling split sepolicy: %s', ' '.join(split_sepolicy_cmd))
common.RunAndCheckOutput(split_sepolicy_cmd) common.RunAndCheckOutput(split_sepolicy_cmd)
# TODO(b/178864050): Run tests on the combined.policy file. # Include the compiled policy in an image if requested.
if rebuild_sepolicy:
rebuild_image_with_sepolicy(output_target_files_temp_dir, vendor_otatools,
vendor_target_files)
# Run validation checks on the pre-installed APEX files. # Run validation checks on the pre-installed APEX files.
validate_merged_apex_info(output_target_files_temp_dir, partition_map.keys()) validate_merged_apex_info(output_target_files_temp_dir, partition_map.keys())
@@ -1261,6 +1361,10 @@ def main():
OPTIONS.rebuild_recovery = True OPTIONS.rebuild_recovery = True
elif o == '--allow-duplicate-apkapex-keys': elif o == '--allow-duplicate-apkapex-keys':
OPTIONS.allow_duplicate_apkapex_keys = True OPTIONS.allow_duplicate_apkapex_keys = True
elif o == '--vendor-otatools':
OPTIONS.vendor_otatools = a
elif o == '--rebuild-sepolicy':
OPTIONS.rebuild_sepolicy = True
elif o == '--keep-tmp': elif o == '--keep-tmp':
OPTIONS.keep_tmp = True OPTIONS.keep_tmp = True
else: else:
@@ -1289,6 +1393,8 @@ def main():
'output-super-empty=', 'output-super-empty=',
'rebuild_recovery', 'rebuild_recovery',
'allow-duplicate-apkapex-keys', 'allow-duplicate-apkapex-keys',
'vendor-otatools=',
'rebuild-sepolicy',
'keep-tmp', 'keep-tmp',
], ],
extra_option_handler=option_handler) extra_option_handler=option_handler)
@@ -1342,7 +1448,9 @@ def main():
output_ota=OPTIONS.output_ota, output_ota=OPTIONS.output_ota,
output_img=OPTIONS.output_img, output_img=OPTIONS.output_img,
output_super_empty=OPTIONS.output_super_empty, output_super_empty=OPTIONS.output_super_empty,
rebuild_recovery=OPTIONS.rebuild_recovery), OPTIONS.keep_tmp) rebuild_recovery=OPTIONS.rebuild_recovery,
vendor_otatools=OPTIONS.vendor_otatools,
rebuild_sepolicy=OPTIONS.rebuild_sepolicy), OPTIONS.keep_tmp)
if __name__ == '__main__': if __name__ == '__main__':

View File

@@ -265,10 +265,10 @@ class MergeTargetFilesTest(test_utils.ReleaseToolsTestCase):
'system': 'system', 'system': 'system',
'product': 'product', 'product': 'product',
'vendor': 'vendor', 'vendor': 'vendor',
}, os.path.join(product_out_dir, 'policy')) })
self.assertEqual(' '.join(cmd), self.assertEqual(' '.join(cmd),
('secilc -m -M true -G -N -c 30 ' ('secilc -m -M true -G -N -c 30 '
'-o {OTP}/policy -f /dev/null ' '-o {OTP}/META/combined_sepolicy -f /dev/null '
'{OTP}/system/etc/selinux/plat_sepolicy.cil ' '{OTP}/system/etc/selinux/plat_sepolicy.cil '
'{OTP}/system/etc/selinux/mapping/30.0.cil ' '{OTP}/system/etc/selinux/mapping/30.0.cil '
'{OTP}/vendor/etc/selinux/vendor_sepolicy.cil ' '{OTP}/vendor/etc/selinux/vendor_sepolicy.cil '