Add support to generate fsverity metadata
By setting fsverity.inputs, android_filesystem can now generate .fsv_meta and BuildManifest.apk. It has been done by Makefile because Makefile is the only one knowing all installed files. But now android_filesystem is aware of all artifacts, so we can move fsverity metadata generation into Soong. Bug: 330282551 Test: m aosp_cf_system_x86_64 and see output Change-Id: Iae4dd83eaede960c263bfba537211df4ff4b36bd
This commit is contained in:
@@ -18,6 +18,7 @@ bootstrap_go_package {
|
||||
"avb_gen_vbmeta_image.go",
|
||||
"bootimg.go",
|
||||
"filesystem.go",
|
||||
"fsverity_metadata.go",
|
||||
"logical_partition.go",
|
||||
"raw_binary.go",
|
||||
"system_image.go",
|
||||
|
@@ -57,7 +57,7 @@ type filesystem struct {
|
||||
output android.OutputPath
|
||||
installDir android.InstallPath
|
||||
|
||||
// For testing. Keeps the result of CopyDepsToZip()
|
||||
// For testing. Keeps the result of CopySpecsToDir()
|
||||
entries []string
|
||||
}
|
||||
|
||||
@@ -120,6 +120,8 @@ type filesystemProperties struct {
|
||||
// modules would be installed to the same location as a make module, they will overwrite
|
||||
// the make version.
|
||||
Include_make_built_files string
|
||||
|
||||
Fsverity fsverityProperties
|
||||
}
|
||||
|
||||
// android_filesystem packages a set of modules and their transitive dependencies into a filesystem
|
||||
@@ -176,6 +178,10 @@ func (f *filesystem) installFileName() string {
|
||||
return f.BaseModuleName() + ".img"
|
||||
}
|
||||
|
||||
func (f *filesystem) partitionName() string {
|
||||
return proptools.StringDefault(f.properties.Partition_name, f.Name())
|
||||
}
|
||||
|
||||
var pctx = android.NewPackageContext("android/soong/filesystem")
|
||||
|
||||
func (f *filesystem) GenerateAndroidBuildActions(ctx android.ModuleContext) {
|
||||
@@ -255,10 +261,12 @@ func (f *filesystem) buildImageUsingBuildImage(ctx android.ModuleContext) androi
|
||||
builder := android.NewRuleBuilder(pctx, ctx)
|
||||
// Wipe the root dir to get rid of leftover files from prior builds
|
||||
builder.Command().Textf("rm -rf %s && mkdir -p %s", rootDir, rootDir)
|
||||
f.entries = f.CopySpecsToDir(ctx, builder, f.gatherFilteredPackagingSpecs(ctx), rebasedDir)
|
||||
specs := f.gatherFilteredPackagingSpecs(ctx)
|
||||
f.entries = f.CopySpecsToDir(ctx, builder, specs, rebasedDir)
|
||||
|
||||
f.buildNonDepsFiles(ctx, builder, rootDir)
|
||||
f.addMakeBuiltFiles(ctx, builder, rootDir)
|
||||
f.buildFsverityMetadataFiles(ctx, builder, specs, rootDir, rebasedDir)
|
||||
|
||||
// run host_init_verifier
|
||||
// Ideally we should have a concept of pluggable linters that verify the generated image.
|
||||
@@ -338,13 +346,12 @@ func (f *filesystem) buildPropFile(ctx android.ModuleContext) (propFile android.
|
||||
addStr("avb_algorithm", algorithm)
|
||||
key := android.PathForModuleSrc(ctx, proptools.String(f.properties.Avb_private_key))
|
||||
addPath("avb_key_path", key)
|
||||
partitionName := proptools.StringDefault(f.properties.Partition_name, f.Name())
|
||||
addStr("partition_name", partitionName)
|
||||
addStr("partition_name", f.partitionName())
|
||||
avb_add_hashtree_footer_args := "--do_not_generate_fec"
|
||||
if hashAlgorithm := proptools.String(f.properties.Avb_hash_algorithm); hashAlgorithm != "" {
|
||||
avb_add_hashtree_footer_args += " --hash_algorithm " + hashAlgorithm
|
||||
}
|
||||
securityPatchKey := "com.android.build." + partitionName + ".security_patch"
|
||||
securityPatchKey := "com.android.build." + f.partitionName() + ".security_patch"
|
||||
securityPatchValue := ctx.Config().PlatformSecurityPatch()
|
||||
avb_add_hashtree_footer_args += " --prop " + securityPatchKey + ":" + securityPatchValue
|
||||
addStr("avb_add_hashtree_footer_args", avb_add_hashtree_footer_args)
|
||||
@@ -388,9 +395,11 @@ func (f *filesystem) buildCpioImage(ctx android.ModuleContext, compressed bool)
|
||||
builder := android.NewRuleBuilder(pctx, ctx)
|
||||
// Wipe the root dir to get rid of leftover files from prior builds
|
||||
builder.Command().Textf("rm -rf %s && mkdir -p %s", rootDir, rootDir)
|
||||
f.entries = f.CopySpecsToDir(ctx, builder, f.gatherFilteredPackagingSpecs(ctx), rebasedDir)
|
||||
specs := f.gatherFilteredPackagingSpecs(ctx)
|
||||
f.entries = f.CopySpecsToDir(ctx, builder, specs, rebasedDir)
|
||||
|
||||
f.buildNonDepsFiles(ctx, builder, rootDir)
|
||||
f.buildFsverityMetadataFiles(ctx, builder, specs, rootDir, rebasedDir)
|
||||
|
||||
output := android.PathForModuleOut(ctx, f.installFileName()).OutputPath
|
||||
cmd := builder.Command().
|
||||
|
166
filesystem/fsverity_metadata.go
Normal file
166
filesystem/fsverity_metadata.go
Normal file
@@ -0,0 +1,166 @@
|
||||
// Copyright (C) 2024 The Android Open Source Project
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package filesystem
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"android/soong/android"
|
||||
)
|
||||
|
||||
type fsverityProperties struct {
|
||||
// Patterns of files for fsverity metadata generation. For each matched file, a .fsv_meta file
|
||||
// will be generated and included to the filesystem image.
|
||||
// etc/security/fsverity/BuildManifest.apk will also be generated which contains information
|
||||
// about generated .fsv_meta files.
|
||||
Inputs []string
|
||||
}
|
||||
|
||||
func (f *filesystem) writeManifestGeneratorListFile(ctx android.ModuleContext, outputPath android.OutputPath, matchedSpecs []android.PackagingSpec, rebasedDir android.OutputPath) {
|
||||
var buf strings.Builder
|
||||
for _, spec := range matchedSpecs {
|
||||
buf.WriteString(rebasedDir.Join(ctx, spec.RelPathInPackage()).String())
|
||||
buf.WriteRune('\n')
|
||||
}
|
||||
android.WriteFileRuleVerbatim(ctx, outputPath, buf.String())
|
||||
}
|
||||
|
||||
func (f *filesystem) buildFsverityMetadataFiles(ctx android.ModuleContext, builder *android.RuleBuilder, specs map[string]android.PackagingSpec, rootDir android.OutputPath, rebasedDir android.OutputPath) {
|
||||
match := func(path string) bool {
|
||||
for _, pattern := range f.properties.Fsverity.Inputs {
|
||||
if matched, err := filepath.Match(pattern, path); matched {
|
||||
return true
|
||||
} else if err != nil {
|
||||
ctx.PropertyErrorf("fsverity.inputs", "bad pattern %q", pattern)
|
||||
return false
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
var matchedSpecs []android.PackagingSpec
|
||||
for _, relPath := range android.SortedKeys(specs) {
|
||||
if match(relPath) {
|
||||
matchedSpecs = append(matchedSpecs, specs[relPath])
|
||||
}
|
||||
}
|
||||
|
||||
if len(matchedSpecs) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
fsverityBuilderPath := android.PathForModuleOut(ctx, "fsverity_builder.sh")
|
||||
metadataGeneratorPath := ctx.Config().HostToolPath(ctx, "fsverity_metadata_generator")
|
||||
fsverityPath := ctx.Config().HostToolPath(ctx, "fsverity")
|
||||
|
||||
cmd := builder.Command().Tool(fsverityBuilderPath)
|
||||
|
||||
// STEP 1: generate .fsv_meta
|
||||
var sb strings.Builder
|
||||
sb.WriteString("set -e\n")
|
||||
cmd.Implicit(metadataGeneratorPath).Implicit(fsverityPath)
|
||||
for _, spec := range matchedSpecs {
|
||||
// srcPath is copied by CopySpecsToDir()
|
||||
srcPath := rebasedDir.Join(ctx, spec.RelPathInPackage())
|
||||
destPath := rebasedDir.Join(ctx, spec.RelPathInPackage()+".fsv_meta")
|
||||
sb.WriteString(metadataGeneratorPath.String())
|
||||
sb.WriteString(" --fsverity-path ")
|
||||
sb.WriteString(fsverityPath.String())
|
||||
sb.WriteString(" --signature none --hash-alg sha256 --output ")
|
||||
sb.WriteString(destPath.String())
|
||||
sb.WriteRune(' ')
|
||||
sb.WriteString(srcPath.String())
|
||||
sb.WriteRune('\n')
|
||||
}
|
||||
|
||||
// STEP 2: generate signed BuildManifest.apk
|
||||
// STEP 2-1: generate build_manifest.pb
|
||||
assetsPath := android.PathForModuleOut(ctx, "fsverity_manifest/assets")
|
||||
manifestPbPath := assetsPath.Join(ctx, "build_manifest.pb")
|
||||
manifestGeneratorPath := ctx.Config().HostToolPath(ctx, "fsverity_manifest_generator")
|
||||
cmd.Implicit(manifestGeneratorPath)
|
||||
sb.WriteString("rm -rf ")
|
||||
sb.WriteString(assetsPath.String())
|
||||
sb.WriteString(" && mkdir -p ")
|
||||
sb.WriteString(assetsPath.String())
|
||||
sb.WriteRune('\n')
|
||||
sb.WriteString(manifestGeneratorPath.String())
|
||||
sb.WriteString(" --fsverity-path ")
|
||||
sb.WriteString(fsverityPath.String())
|
||||
sb.WriteString(" --base-dir ")
|
||||
sb.WriteString(rootDir.String())
|
||||
sb.WriteString(" --output ")
|
||||
sb.WriteString(manifestPbPath.String())
|
||||
sb.WriteRune(' ')
|
||||
|
||||
manifestGeneratorListPath := android.PathForModuleOut(ctx, "fsverity_manifest.list")
|
||||
f.writeManifestGeneratorListFile(ctx, manifestGeneratorListPath.OutputPath, matchedSpecs, rebasedDir)
|
||||
sb.WriteRune('@')
|
||||
sb.WriteString(manifestGeneratorListPath.String())
|
||||
sb.WriteRune('\n')
|
||||
cmd.Implicit(manifestGeneratorListPath)
|
||||
|
||||
// STEP 2-2: generate BuildManifest.apk (unsigned)
|
||||
aapt2Path := ctx.Config().HostToolPath(ctx, "aapt2")
|
||||
apkPath := rebasedDir.Join(ctx, "etc", "security", "fsverity", "BuildManifest.apk")
|
||||
manifestTemplatePath := android.PathForSource(ctx, "system/security/fsverity/AndroidManifest.xml")
|
||||
// package-export is currently generated by Makefile.
|
||||
// TODO(b/330282551): fully migrate into Soong
|
||||
frameworkResPath := android.PathForArbitraryOutput(ctx, "target/common/obj/APPS/framework-res_intermediates/package-export.apk")
|
||||
cmd.Implicit(aapt2Path)
|
||||
cmd.Implicit(manifestTemplatePath)
|
||||
cmd.Implicit(frameworkResPath)
|
||||
|
||||
sb.WriteString(aapt2Path.String())
|
||||
sb.WriteString(" link -o ")
|
||||
sb.WriteString(apkPath.String())
|
||||
sb.WriteString(" -A ")
|
||||
sb.WriteString(assetsPath.String())
|
||||
sb.WriteString(" -I ")
|
||||
sb.WriteString(frameworkResPath.String())
|
||||
minSdkVersion := ctx.Config().PlatformSdkCodename()
|
||||
if minSdkVersion == "REL" {
|
||||
minSdkVersion = ctx.Config().PlatformSdkVersion().String()
|
||||
}
|
||||
sb.WriteString(" --min-sdk-version ")
|
||||
sb.WriteString(minSdkVersion)
|
||||
sb.WriteString(" --version-code ")
|
||||
sb.WriteString(ctx.Config().PlatformSdkVersion().String())
|
||||
sb.WriteString(" --version-name ")
|
||||
sb.WriteString(ctx.Config().AppsDefaultVersionName())
|
||||
sb.WriteString(" --manifest ")
|
||||
sb.WriteString(manifestTemplatePath.String())
|
||||
sb.WriteString(" --rename-manifest-package com.android.security.fsverity_metadata.")
|
||||
sb.WriteString(f.partitionName())
|
||||
sb.WriteRune('\n')
|
||||
|
||||
// STEP 2-3: sign BuildManifest.apk
|
||||
apksignerPath := ctx.Config().HostToolPath(ctx, "apksigner")
|
||||
pemPath, keyPath := ctx.Config().DefaultAppCertificate(ctx)
|
||||
cmd.Implicit(apksignerPath)
|
||||
cmd.Implicit(pemPath)
|
||||
cmd.Implicit(keyPath)
|
||||
sb.WriteString(apksignerPath.String())
|
||||
sb.WriteString(" sign --in ")
|
||||
sb.WriteString(apkPath.String())
|
||||
sb.WriteString(" --cert ")
|
||||
sb.WriteString(pemPath.String())
|
||||
sb.WriteString(" --key ")
|
||||
sb.WriteString(keyPath.String())
|
||||
sb.WriteRune('\n')
|
||||
|
||||
android.WriteExecutableFileRuleVerbatim(ctx, fsverityBuilderPath, sb.String())
|
||||
}
|
Reference in New Issue
Block a user