diff options
-rw-r--r-- | apexer/Android.bp | 2 | ||||
-rw-r--r-- | apexer/apexer.py | 175 | ||||
-rwxr-xr-x | apexer/runtests.sh | 13 | ||||
-rw-r--r-- | proto/apex_build_info.proto | 3 |
4 files changed, 129 insertions, 64 deletions
diff --git a/apexer/Android.bp b/apexer/Android.bp index a33aade..271e678 100644 --- a/apexer/Android.bp +++ b/apexer/Android.bp @@ -23,6 +23,8 @@ apexer_tools = [ "sefcontext_compile", "soong_zip", "zipalign", + "make_f2fs", + "sload_f2fs", // TODO(b/124476339) apex doesn't follow 'required' dependencies so we need to include this // manually for 'avbtool'. "fec", diff --git a/apexer/apexer.py b/apexer/apexer.py index f698c8c..5d4d5c2 100644 --- a/apexer/apexer.py +++ b/apexer/apexer.py @@ -101,6 +101,13 @@ def ParseArgs(argv): choices=['zip', 'image'], help='type of APEX payload being built "zip" or "image"') parser.add_argument( + '--payload_fs_type', + metavar='FS_TYPE', + required=False, + default='ext4', + choices=['ext4', 'f2fs'], + help='type of filesystem being used for payload image "ext4" or "f2fs"') + parser.add_argument( '--override_apk_package_name', required=False, help='package name of the APK container. Default is the apex name in --manifest.' @@ -162,7 +169,7 @@ def ParseArgs(argv): parser.add_argument( '--unsigned_payload_only', action='store_true', - help="""Outputs the unsigned payload image/zip only. Also, setting this flag implies + help="""Outputs the unsigned payload image/zip only. Also, setting this flag implies --payload_only is set too.""" ) parser.add_argument( @@ -182,7 +189,7 @@ def FindBinaryPath(binary): ':'.join(tool_path_list)) -def RunCommand(cmd, verbose=False, env=None): +def RunCommand(cmd, verbose=False, env=None, expected_return_values={0}): env = env or {} env.update(os.environ.copy()) @@ -194,10 +201,10 @@ def RunCommand(cmd, verbose=False, env=None): cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, env=env) output, _ = p.communicate() - if verbose or p.returncode is not 0: + if verbose or p.returncode not in expected_return_values: print(output.rstrip()) - assert p.returncode is 0, 'Failed to execute: ' + ' '.join(cmd) + assert p.returncode in expected_return_values, 'Failed to execute: ' + ' '.join(cmd) return (output, p.returncode) @@ -378,6 +385,9 @@ def GenerateBuildInfo(args): if args.logging_parent: build_info.logging_parent = args.logging_parent + if args.payload_type == 'image': + build_info.payload_fs_type = args.payload_fs_type + return build_info def AddLoggingParent(android_manifest, logging_parent_value): @@ -450,9 +460,8 @@ def CreateApex(args, work_dir): print("Cannot read manifest file: '" + args.manifest + "'") return False - # create an empty ext4 image that is sufficiently big - # sufficiently big = size + 16MB margin - size_in_mb = (GetDirSize(args.input_dir) / (1024 * 1024)) + 16 + # create an empty image that is sufficiently big + size_in_mb = (GetDirSize(args.input_dir) / (1024 * 1024)) content_dir = os.path.join(work_dir, 'content') os.mkdir(content_dir) @@ -479,61 +488,105 @@ def CreateApex(args, work_dir): return False img_file = os.path.join(content_dir, 'apex_payload.img') - # margin is for files that are not under args.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 - - cmd = ['mke2fs'] - cmd.extend(['-O', '^has_journal']) # because image is read-only - cmd.extend(['-b', str(BLOCK_SIZE)]) - cmd.extend(['-m', '0']) # reserved block percentage - cmd.extend(['-t', 'ext4']) - cmd.extend(['-I', '256']) # inode size - cmd.extend(['-N', str(inode_num)]) - uu = str(uuid.uuid5(uuid.NAMESPACE_URL, 'www.android.com')) - cmd.extend(['-U', uu]) - cmd.extend(['-E', 'hash_seed=' + uu]) - cmd.append(img_file) - cmd.append(str(size_in_mb) + 'M') - RunCommand(cmd, args.verbose, {'E2FSPROGS_FAKE_TIME': '1'}) - - # Compile the file context into the binary form - compiled_file_contexts = os.path.join(work_dir, 'file_contexts.bin') - cmd = ['sefcontext_compile'] - cmd.extend(['-o', compiled_file_contexts]) - cmd.append(args.file_contexts) - RunCommand(cmd, args.verbose) + if args.payload_fs_type == 'ext4': + # sufficiently big = size + 16MB margin + size_in_mb += 16 + + # margin is for files that are not under args.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 + + cmd = ['mke2fs'] + cmd.extend(['-O', '^has_journal']) # because image is read-only + cmd.extend(['-b', str(BLOCK_SIZE)]) + cmd.extend(['-m', '0']) # reserved block percentage + cmd.extend(['-t', 'ext4']) + cmd.extend(['-I', '256']) # inode size + cmd.extend(['-N', str(inode_num)]) + uu = str(uuid.uuid5(uuid.NAMESPACE_URL, 'www.android.com')) + cmd.extend(['-U', uu]) + cmd.extend(['-E', 'hash_seed=' + uu]) + cmd.append(img_file) + cmd.append(str(size_in_mb) + 'M') + RunCommand(cmd, args.verbose, {'E2FSPROGS_FAKE_TIME': '1'}) + + # Compile the file context into the binary form + compiled_file_contexts = os.path.join(work_dir, 'file_contexts.bin') + cmd = ['sefcontext_compile'] + cmd.extend(['-o', compiled_file_contexts]) + cmd.append(args.file_contexts) + RunCommand(cmd, args.verbose) + + # 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(['-T', '0']) # time is set to epoch + cmd.extend(['-S', compiled_file_contexts]) + cmd.extend(['-C', args.canned_fs_config]) + cmd.append('-s') # share dup blocks + cmd.append(img_file) + RunCommand(cmd, args.verbose, {'E2FSPROGS_FAKE_TIME': '1'}) + + cmd = ['e2fsdroid'] + cmd.append('-e') # input is not android_sparse_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.append('-s') # share dup blocks + cmd.append(img_file) + RunCommand(cmd, args.verbose, {'E2FSPROGS_FAKE_TIME': '1'}) + + # Resize the image file to save space + cmd = ['resize2fs'] + cmd.append('-M') # shrink as small as possible + cmd.append(img_file) + RunCommand(cmd, args.verbose, {'E2FSPROGS_FAKE_TIME': '1'}) + + elif args.payload_fs_type == 'f2fs': + # 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 += 100 + + # Create an empty image + cmd = ['/usr/bin/fallocate'] + cmd.extend(['-l', str(size_in_mb)+'M']) + cmd.append(img_file) + RunCommand(cmd, args.verbose) + + # Format the image to F2FS + cmd = ['make_f2fs'] + cmd.extend(['-g', 'android']) + uu = str(uuid.uuid5(uuid.NAMESPACE_URL, 'www.android.com')) + cmd.extend(['-U', uu]) + cmd.extend(['-T', '0']) + cmd.append('-r') # sets checkpointing seed to 0 to remove random bits + cmd.append(img_file) + RunCommand(cmd, args.verbose) - # 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(['-T', '0']) # time is set to epoch - cmd.extend(['-S', compiled_file_contexts]) - cmd.extend(['-C', args.canned_fs_config]) - cmd.append('-s') # share dup blocks - cmd.append(img_file) - RunCommand(cmd, args.verbose, {'E2FSPROGS_FAKE_TIME': '1'}) - - cmd = ['e2fsdroid'] - cmd.append('-e') # input is not android_sparse_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.append('-s') # share dup blocks - cmd.append(img_file) - RunCommand(cmd, args.verbose, {'E2FSPROGS_FAKE_TIME': '1'}) - - # Resize the image file to save space - cmd = ['resize2fs'] - cmd.append('-M') # shrink as small as possible - cmd.append(img_file) - RunCommand(cmd, args.verbose, {'E2FSPROGS_FAKE_TIME': '1'}) + # Add files to the image + cmd = ['sload_f2fs'] + cmd.extend(['-C', args.canned_fs_config]) + cmd.extend(['-f', manifests_dir]) + cmd.extend(['-s', args.file_contexts]) + cmd.extend(['-T', '0']) + cmd.append(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(['-s', args.file_contexts]) + cmd.extend(['-T', '0']) + cmd.append(img_file) + RunCommand(cmd, args.verbose, expected_return_values={0,1}) + + # TODO(b/158453869): resize the image file to save space if args.unsigned_payload_only: shutil.copyfile(img_file, args.output) diff --git a/apexer/runtests.sh b/apexer/runtests.sh index 7a499c7..201270e 100755 --- a/apexer/runtests.sh +++ b/apexer/runtests.sh @@ -28,10 +28,12 @@ m -j apexer export APEXER_TOOL_PATH="${ANDROID_BUILD_TOP}/out/soong/host/linux-x86/bin:${ANDROID_BUILD_TOP}/prebuilts/sdk/tools/linux/bin" PATH+=":${ANDROID_BUILD_TOP}/prebuilts/sdk/tools/linux/bin" +for fs_type in ext4 f2fs +do input_dir=$(mktemp -d) output_dir=$(mktemp -d) -function finish { +function cleanup { sudo umount /dev/loop10 sudo losetup --detach /dev/loop10 @@ -39,7 +41,7 @@ function finish { rm -rf ${output_dir} } -trap finish EXIT +trap cleanup ERR ############################################# # prepare the inputs ############################################# @@ -82,6 +84,7 @@ output_file=${output_dir}/test.apex ${ANDROID_HOST_OUT}/bin/apexer --verbose --manifest ${manifest_file} \ --file_contexts ${file_contexts_file} \ --canned_fs_config ${canned_fs_config_file} \ + --payload_fs_type ${fs_type} \ --key ${ANDROID_BUILD_TOP}/system/apex/apexer/testdata/com.android.example.apex.pem \ ${input_dir} ${output_file} @@ -131,4 +134,8 @@ sudo diff ${input_dir}/sub/file3 ${output_dir}/mnt/sub/file3 # check the android manifest aapt dump xmltree ${output_file} AndroidManifest.xml -echo Passed +echo "Passed for ${fs_type}" +cleanup +done + +echo "Passed for all fs types" diff --git a/proto/apex_build_info.proto b/proto/apex_build_info.proto index e25baa2..fd8a349 100644 --- a/proto/apex_build_info.proto +++ b/proto/apex_build_info.proto @@ -48,4 +48,7 @@ message ApexBuildInfo { // Value of --logging_parent passed at build time. string logging_parent = 9; + + // Value of --payload_fs_type passed at build time. + string payload_fs_type = 10; } |