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
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 tempoary files for debugging purposes.
"""
@ -129,6 +137,8 @@ OPTIONS.output_super_empty = None
OPTIONS.rebuild_recovery = False
# TODO(b/150582573): Remove this option.
OPTIONS.allow_duplicate_apkapex_keys = False
OPTIONS.vendor_otatools = None
OPTIONS.rebuild_sepolicy = False
OPTIONS.keep_tmp = False
# 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'))
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.
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:
product_out: PRODUCT_OUT directory, containing partition directories.
partition_map: A map of partition name -> relative path within product_out.
output_policy: The name of the output policy created by secilc.
Returns:
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().
cmd = ['secilc', '-m', '-M', 'true', '-G', '-N']
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'])
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.
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.
Raises:
@ -965,6 +975,92 @@ def generate_images(target_files_dir, rebuild_recovery):
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):
"""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,
vendor_item_list, output_target_files, output_dir,
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.
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.
rebuild_recovery: If true, rebuild the recovery patch used by non-A/B
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',
@ -1137,14 +1237,14 @@ def merge_target_files(temp_dir, framework_target_files, framework_item_list,
partition_map=filtered_partitions)
# Check that the split sepolicy from the multiple builds can compile.
split_sepolicy_cmd = compile_split_sepolicy(
product_out=output_target_files_temp_dir,
partition_map=filtered_partitions,
output_policy=os.path.join(output_target_files_temp_dir,
'META/combined.policy'))
split_sepolicy_cmd = compile_split_sepolicy(output_target_files_temp_dir,
filtered_partitions)
logger.info('Compiling split sepolicy: %s', ' '.join(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.
validate_merged_apex_info(output_target_files_temp_dir, partition_map.keys())
@ -1261,6 +1361,10 @@ def main():
OPTIONS.rebuild_recovery = True
elif o == '--allow-duplicate-apkapex-keys':
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':
OPTIONS.keep_tmp = True
else:
@ -1289,6 +1393,8 @@ def main():
'output-super-empty=',
'rebuild_recovery',
'allow-duplicate-apkapex-keys',
'vendor-otatools=',
'rebuild-sepolicy',
'keep-tmp',
],
extra_option_handler=option_handler)
@ -1342,7 +1448,9 @@ def main():
output_ota=OPTIONS.output_ota,
output_img=OPTIONS.output_img,
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__':

View file

@ -265,10 +265,10 @@ class MergeTargetFilesTest(test_utils.ReleaseToolsTestCase):
'system': 'system',
'product': 'product',
'vendor': 'vendor',
}, os.path.join(product_out_dir, 'policy'))
})
self.assertEqual(' '.join(cmd),
('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/mapping/30.0.cil '
'{OTP}/vendor/etc/selinux/vendor_sepolicy.cil '