summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2022-04-26 19:10:41 +0000
committerAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2022-04-26 19:10:41 +0000
commit97ba350dad734a6e032bedf0618eb8061e027a8b (patch)
treead762b6469d45c133aaf9ced9869edb39048ac4e
parent006bacca91b832af7d42b8d038bb71ae76d70996 (diff)
parent0f12f5c9b349ca0f5e9facec2789f20825b02d62 (diff)
downloadapex-97ba350dad734a6e032bedf0618eb8061e027a8b.tar.gz
Snap for 8502205 from 0f12f5c9b349ca0f5e9facec2789f20825b02d62 to main-cg-testing-release
Change-Id: I36256b4c3cceddaba985628d95bc129f1339d91a
-rw-r--r--apexer/apexer.py165
-rwxr-xr-xapexer/runtests.sh9
2 files changed, 134 insertions, 40 deletions
diff --git a/apexer/apexer.py b/apexer/apexer.py
index 05940935..71b3a948 100644
--- a/apexer/apexer.py
+++ b/apexer/apexer.py
@@ -187,6 +187,12 @@ def ParseArgs(argv):
'Add testOnly=true attribute to application element in '
'AndroidManifest file.')
)
+ parser.add_argument(
+ '--apex_version_placeholder',
+ default = "__APEX_VERSION_PLACEHOLDER__",
+ required=False,
+ help='Default placeholder string in the APEX directory paths to be' +
+ 'replaced with the APEX version code.')
return parser.parse_args(argv)
@@ -346,14 +352,13 @@ def ValidateArgs(args):
return False
if not args.canned_fs_config:
- if not args.canned_fs_config:
- if build_info is not None:
- with tempfile.NamedTemporaryFile(delete=False) as temp:
- temp.write(build_info.canned_fs_config)
- args.canned_fs_config = temp.name
- else:
- print('Missing ----canned_fs_config {config} argument, or a --build_info argument!')
- return False
+ if build_info is not None:
+ with tempfile.NamedTemporaryFile(delete=False) as temp:
+ temp.write(build_info.canned_fs_config)
+ args.canned_fs_config = temp.name
+ else:
+ print('Missing --canned_fs_config {config} argument, or a --build_info argument!')
+ return False
if not args.target_sdk_version:
if build_info is not None:
@@ -383,18 +388,18 @@ def ValidateArgs(args):
return True
-def GenerateBuildInfo(args):
+def GenerateBuildInfo(args, file_contexts, canned_fs_config, android_manifest):
build_info = apex_build_info_pb2.ApexBuildInfo()
if (args.include_cmd_line_in_build_info):
build_info.apexer_command_line = str(sys.argv)
- with open(args.file_contexts, 'rb') as f:
+ with open(file_contexts, 'rb') as f:
build_info.file_contexts = f.read()
- with open(args.canned_fs_config, 'rb') as f:
+ with open(canned_fs_config, 'rb') as f:
build_info.canned_fs_config = f.read()
- with open(args.android_manifest, 'rb') as f:
+ with open(android_manifest, 'rb') as f:
build_info.android_manifest = f.read()
if args.target_sdk_version:
@@ -480,19 +485,19 @@ def ShaHashFiles(file_paths):
return h.hexdigest()
-def CreateImageExt4(args, work_dir, manifests_dir, img_file):
+def CreateImageExt4(args, staging_input_dir, work_dir, manifests_dir, img_file, canned_fs_config):
"""Create image for ext4 file system."""
# sufficiently big = size + 16MB margin
- size_in_mb = (GetDirSize(args.input_dir) // (1024 * 1024))
+ size_in_mb = (GetDirSize(staging_input_dir) // (1024 * 1024))
size_in_mb += 16
- # Margin is for files that are not under args.input_dir. this consists of
+ # Margin is for files that are not under staging_input_dir. this consists of
# n inodes for apex_manifest files and 11 reserved inodes for ext4.
# TOBO(b/122991714) eliminate these details. Use build_image.py which
# determines the optimal inode count by first building an image and then
# count the inodes actually used.
inode_num_margin = GetFilesAndDirsCount(manifests_dir) + 11
- inode_num = GetFilesAndDirsCount(args.input_dir) + inode_num_margin
+ inode_num = GetFilesAndDirsCount(staging_input_dir) + inode_num_margin
cmd = ['mke2fs']
cmd.extend(['-O', '^has_journal']) # because image is read-only
@@ -524,10 +529,10 @@ def CreateImageExt4(args, work_dir, manifests_dir, img_file):
# Add files to the image file
cmd = ['e2fsdroid']
cmd.append('-e') # input is not android_sparse_file
- cmd.extend(['-f', args.input_dir])
+ cmd.extend(['-f', staging_input_dir])
cmd.extend(['-T', '0']) # time is set to epoch
cmd.extend(['-S', compiled_file_contexts])
- cmd.extend(['-C', args.canned_fs_config])
+ cmd.extend(['-C', canned_fs_config])
cmd.extend(['-a', '/'])
cmd.append('-s') # share dup blocks
cmd.append(img_file)
@@ -538,7 +543,7 @@ def CreateImageExt4(args, work_dir, manifests_dir, img_file):
cmd.extend(['-f', manifests_dir])
cmd.extend(['-T', '0']) # time is set to epoch
cmd.extend(['-S', compiled_file_contexts])
- cmd.extend(['-C', args.canned_fs_config])
+ cmd.extend(['-C', canned_fs_config])
cmd.extend(['-a', '/'])
cmd.append('-s') # share dup blocks
cmd.append(img_file)
@@ -551,12 +556,12 @@ def CreateImageExt4(args, work_dir, manifests_dir, img_file):
RunCommand(cmd, args.verbose, {'E2FSPROGS_FAKE_TIME': '1'})
-def CreateImageF2fs(args, manifests_dir, img_file):
+def CreateImageF2fs(args, staging_input_dir, manifests_dir, img_file, canned_fs_config):
"""Create image for f2fs file system."""
# F2FS requires a ~100M minimum size (necessary for ART, could be reduced
# a bit for other)
# TODO(b/158453869): relax these requirements for readonly devices
- size_in_mb = (GetDirSize(args.input_dir) // (1024 * 1024))
+ size_in_mb = (GetDirSize(staging_input_dir) // (1024 * 1024))
size_in_mb += 100
# Create an empty image
@@ -577,7 +582,7 @@ def CreateImageF2fs(args, manifests_dir, img_file):
# Add files to the image
cmd = ['sload_f2fs']
- cmd.extend(['-C', args.canned_fs_config])
+ cmd.extend(['-C', canned_fs_config])
cmd.extend(['-f', manifests_dir])
cmd.extend(['-s', args.file_contexts])
cmd.extend(['-T', '0'])
@@ -585,8 +590,8 @@ def CreateImageF2fs(args, manifests_dir, img_file):
RunCommand(cmd, args.verbose, expected_return_values={0, 1})
cmd = ['sload_f2fs']
- cmd.extend(['-C', args.canned_fs_config])
- cmd.extend(['-f', args.input_dir])
+ cmd.extend(['-C', canned_fs_config])
+ cmd.extend(['-f', staging_input_dir])
cmd.extend(['-s', args.file_contexts])
cmd.extend(['-T', '0'])
cmd.append(img_file)
@@ -595,7 +600,7 @@ def CreateImageF2fs(args, manifests_dir, img_file):
# TODO(b/158453869): resize the image file to save space
-def CreateImageErofs(args, work_dir, manifests_dir, img_file):
+def CreateImageErofs(args, staging_input_dir, work_dir, manifests_dir, img_file, canned_fs_config):
"""Create image for erofs file system."""
# mkfs.erofs doesn't support multiple input
@@ -603,13 +608,13 @@ def CreateImageErofs(args, work_dir, manifests_dir, img_file):
os.mkdir(tmp_input_dir)
cmd = ['/bin/cp', '-ra']
cmd.extend(glob.glob(manifests_dir + '/*'))
- cmd.extend(glob.glob(args.input_dir + '/*'))
+ cmd.extend(glob.glob(staging_input_dir + '/*'))
cmd.append(tmp_input_dir)
RunCommand(cmd, args.verbose)
cmd = ['make_erofs']
cmd.extend(['-z', 'lz4hc'])
- cmd.extend(['--fs-config-file', args.canned_fs_config])
+ cmd.extend(['--fs-config-file', canned_fs_config])
cmd.extend(['--file-contexts', args.file_contexts])
uu = str(uuid.uuid5(uuid.NAMESPACE_URL, 'www.android.com'))
cmd.extend(['-U', uu])
@@ -628,14 +633,14 @@ def CreateImageErofs(args, work_dir, manifests_dir, img_file):
RunCommand(cmd, verbose=False)
-def CreateImage(args, work_dir, manifests_dir, img_file):
+def CreateImage(args, staging_input_dir, work_dir, manifests_dir, img_file, canned_fs_config):
"""create payload image."""
if args.payload_fs_type == 'ext4':
- CreateImageExt4(args, work_dir, manifests_dir, img_file)
+ CreateImageExt4(args, staging_input_dir, work_dir, manifests_dir, img_file, canned_fs_config)
elif args.payload_fs_type == 'f2fs':
- CreateImageF2fs(args, manifests_dir, img_file)
+ CreateImageF2fs(args, staging_input_dir, manifests_dir, img_file, canned_fs_config)
elif args.payload_fs_type == 'erofs':
- CreateImageErofs(args, work_dir, manifests_dir, img_file)
+ CreateImageErofs(args, staging_input_dir, work_dir, manifests_dir, img_file, canned_fs_config)
def SignImage(args, manifest_apex, img_file):
@@ -688,31 +693,39 @@ def SignImage(args, manifest_apex, img_file):
RunCommand(cmd, args.verbose)
-def CreateApexPayload(args, work_dir, content_dir, manifests_dir,
- manifest_apex):
+def CreateApexPayload(
+ args,
+ staging_input_dir,
+ work_dir,
+ content_dir,
+ manifests_dir,
+ manifest_apex,
+ canned_fs_config):
"""Create payload.
Args:
args: apexer options
+ staging_input_dir: the input directory to be turned into the payload
work_dir: apex container working directory
content_dir: the working directory for payload contents
manifests_dir: manifests directory
manifest_apex: apex manifest proto
+ canned_fs_config: the canned fs_config file
Returns:
payload file
"""
if args.payload_type == 'image':
img_file = os.path.join(content_dir, 'apex_payload.img')
- CreateImage(args, work_dir, manifests_dir, img_file)
+ CreateImage(args, staging_input_dir, work_dir, manifests_dir, img_file, canned_fs_config)
if not args.unsigned_payload:
SignImage(args, manifest_apex, img_file)
else:
img_file = os.path.join(content_dir, 'apex_payload.zip')
cmd = ['soong_zip']
cmd.extend(['-o', img_file])
- cmd.extend(['-C', args.input_dir])
- cmd.extend(['-D', args.input_dir])
+ cmd.extend(['-C', staging_input_dir])
+ cmd.extend(['-D', staging_input_dir])
cmd.extend(['-C', manifests_dir])
cmd.extend(['-D', manifests_dir])
RunCommand(cmd, args.verbose)
@@ -750,6 +763,59 @@ def CreateAndroidManifestXml(args, work_dir, manifest_apex):
args.logging_parent)
return android_manifest_file
+def ReplaceApexVersionPlaceholder(args, work_dir, apex_version_code):
+ """Replace args.apex_version_placeholder strings in APEX input paths with the version.
+
+ See b/229574810 for more information.
+
+ Args:
+ args: apexer options
+ work_dir: apex container working directory
+ apex_version_code: apex version as a string
+
+ Returns:
+ a tuple of the new canned_fs_config and input directory with substituted input paths
+ """
+
+ # While manifest_apex.version is an int arg, let's be defensive here
+ # and not rely on that being always the case. Check that the version
+ # is a valid path fragment.
+ version_re = r'^[\w\.\-\_]+$'
+ if not re.match(version_re, apex_version_code, re.ASCII):
+ raise Exception('Unable to use apex verson ' + apex_version_code +
+ ' as filename suffix, valid characters are [a-zA-Z0-9_.-]')
+
+ # Update the canned fs config since it contains entries for every file/dir
+ # in the APEX.
+ if args.canned_fs_config is not None:
+ with open(args.canned_fs_config, 'r') as f:
+ canned_fs_config_content = f.read().replace(
+ args.apex_version_placeholder, apex_version_code)
+ new_canned_fs_config = os.path.join(work_dir, 'canned_fs_config')
+ with open(new_canned_fs_config, 'w') as f:
+ f.write(canned_fs_config_content)
+ else:
+ # TODO(b/193473780): Zip apexes do not have canned fs_config files.
+ # Delete when zipapex is deprecated.
+ new_canned_fs_config = None
+
+ # Copy the input dir into a staging area in the working directory. This is
+ # necessary to perform any apexer-level changes on the file layouts, while
+ # avoiding in-place changes to the real inputs.
+ staging_input_dir = os.path.join(work_dir, 'input')
+ os.makedirs(staging_input_dir, exist_ok=True)
+ for root, _, files in os.walk(args.input_dir):
+ root_relative = os.path.relpath(root, args.input_dir)
+ root_relative = root_relative.replace(
+ args.apex_version_placeholder, apex_version_code)
+ for f in files:
+ src = os.path.join(root, f)
+ dest = os.path.normpath(os.path.join(staging_input_dir, root_relative, f))
+ # APEX contents can be unresolved symlinks, so don't follow them.
+ os.makedirs(os.path.dirname(dest), exist_ok=True)
+ shutil.copy2(src, dest, follow_symlinks=False)
+
+ return (new_canned_fs_config, staging_input_dir)
def CreateApex(args, work_dir):
if not ValidateArgs(args):
@@ -800,9 +866,24 @@ def CreateApex(args, work_dir):
CopyFile(args.manifest_json,
os.path.join(content_dir, 'apex_manifest.json'))
+ apex_version_code = str(manifest_apex.version)
+
+ # b/229574810: replace all instances of args.apex_version_placeholder
+ # strings in /app and /priv-app inputs. This is necessary for the
+ # package manager to correctly invalidate its directory-path based
+ # cache keys and differentiate APKs based on their version codes.
+ canned_fs_config, staging_input_dir = ReplaceApexVersionPlaceholder(
+ args, work_dir, apex_version_code)
+
# Create payload
- img_file = CreateApexPayload(args, work_dir, content_dir, manifests_dir,
- manifest_apex)
+ img_file = CreateApexPayload(
+ args,
+ staging_input_dir,
+ work_dir,
+ content_dir,
+ manifests_dir,
+ manifest_apex,
+ canned_fs_config)
if args.unsigned_payload_only or args.payload_only:
shutil.copyfile(img_file, args.output)
@@ -818,7 +899,11 @@ def CreateApex(args, work_dir):
shutil.copyfile(args.pubkey, os.path.join(content_dir, 'apex_pubkey'))
if args.include_build_info:
- build_info = GenerateBuildInfo(args)
+ build_info = GenerateBuildInfo(
+ args,
+ args.file_contexts,
+ canned_fs_config,
+ args.android_manifest)
with open(os.path.join(content_dir, 'apex_build_info.pb'), 'wb') as f:
f.write(build_info.SerializeToString())
@@ -830,7 +915,7 @@ def CreateApex(args, work_dir):
cmd.extend(['--rename-manifest-package', args.override_apk_package_name])
# This version from apex_manifest.json is used when versionCode isn't
# specified in AndroidManifest.xml
- cmd.extend(['--version-code', str(manifest_apex.version)])
+ cmd.extend(['--version-code', apex_version_code])
if manifest_apex.versionName:
cmd.extend(['--version-name', manifest_apex.versionName])
if args.target_sdk_version:
diff --git a/apexer/runtests.sh b/apexer/runtests.sh
index 4d9d6029..d15bc5c2 100755
--- a/apexer/runtests.sh
+++ b/apexer/runtests.sh
@@ -51,6 +51,8 @@ head -c 1M </dev/urandom > ${input_dir}/file1
head -c 1M </dev/urandom > ${input_dir}/file2
mkdir ${input_dir}/sub
head -c 1M </dev/urandom > ${input_dir}/sub/file3
+mkdir ${input_dir}/sub2@__APEX_VERSION_PLACEHOLDER__
+head -c 1M </dev/urandom > ${input_dir}/sub2@__APEX_VERSION_PLACEHOLDER__/file4
ln -s file1 ${input_dir}/sym1
# Create the APEX manifest file
@@ -64,6 +66,7 @@ file_contexts_file=$(mktemp)
echo '
(/.*)? u:object_r:root_file:s0
/sub(/.*)? u:object_r:sub_file:s0
+/sub2(/.*)? u:object_r:sub2_file:s0
/sub/file3 u:object_r:file3_file:s0
' > ${file_contexts_file}
@@ -74,6 +77,8 @@ echo '/ 1000 1000 0644
/file2 1001 1001 0644
/sub 1002 1002 0644
/sub/file3 1003 1003 0644
+/sub2@__APEX_VERSION_PLACEHOLDER__ 1004 1004 0644
+/sub2@__APEX_VERSION_PLACEHOLDER__/file4 1005 1005 0644
/sym1 1001 1001 0644' > ${canned_fs_config_file}
output_file=${output_dir}/test.apex
@@ -114,6 +119,7 @@ sudo diff ${manifest_file} ${output_dir}/apex_manifest.pb
sudo diff ${input_dir}/file1 ${output_dir}/mnt/file1
sudo diff ${input_dir}/file2 ${output_dir}/mnt/file2
sudo diff ${input_dir}/sub/file3 ${output_dir}/mnt/sub/file3
+sudo diff ${input_dir}/sub2@__APEX_VERSION_PLACEHOLDER__/file4 ${output_dir}/mnt/sub2@1/file4
[ `sudo readlink ${output_dir}/mnt/sym1` = "file1" ]
# check the uid/gid/type/mod
@@ -121,6 +127,8 @@ sudo diff ${input_dir}/sub/file3 ${output_dir}/mnt/sub/file3
[ `sudo stat -c '%u,%g,%A' ${output_dir}/mnt/file2` = "1001,1001,-rw-r--r--" ]
[ `sudo stat -c '%u,%g,%A' ${output_dir}/mnt/sub` = "1002,1002,drw-r--r--" ]
[ `sudo stat -c '%u,%g,%A' ${output_dir}/mnt/sub/file3` = "1003,1003,-rw-r--r--" ]
+[ `sudo stat -c '%u,%g,%A' ${output_dir}/mnt/sub2@1` = "1004,1004,drw-r--r--" ]
+[ `sudo stat -c '%u,%g,%A' ${output_dir}/mnt/sub2@1/file4` = "1005,1005,-rw-r--r--" ]
[ `sudo stat -c '%u,%g,%A' ${output_dir}/mnt/sym1` = "1001,1001,lrw-r--r--" ]
[ `sudo stat -c '%u,%g,%A' ${output_dir}/mnt/apex_manifest.pb` = "1000,1000,-rw-r--r--" ]
@@ -129,6 +137,7 @@ sudo diff ${input_dir}/sub/file3 ${output_dir}/mnt/sub/file3
[ `sudo ls -Z ${output_dir}/mnt/file2 | cut -d ' ' -f 1` = "u:object_r:root_file:s0" ]
[ `sudo ls -d -Z ${output_dir}/mnt/sub/ | cut -d ' ' -f 1` = "u:object_r:sub_file:s0" ]
[ `sudo ls -Z ${output_dir}/mnt/sub/file3 | cut -d ' ' -f 1` = "u:object_r:file3_file:s0" ]
+[ `sudo ls -Z ${output_dir}/mnt/sub2@1/file4 | cut -d ' ' -f 1` = "u:object_r:sub2_file:s0" ]
[ `sudo ls -Z ${output_dir}/mnt/apex_manifest.pb | cut -d ' ' -f 1` = "u:object_r:root_file:s0" ]
[ `sudo ls -Z ${output_dir}/mnt/sym1 | cut -d ' ' -f 1` = "u:object_r:root_file:s0" ]