Make fsverity files able to be built independently
Right now, fsverity files are built when building the staging directory.
This is slow, before this cl it takes over a minute to generate the
staging directory, after it's only ~10 seconds.
Fsverity files are now generated before the staging directory is built,
in independant ninja actions.
Bug: 394404628
Test: m aosp_shared_system_image, verified it didn't change before/after
Change-Id: I9ebc40cd243888e3cb778487751937ccd79093bd
diff --git a/android/packaging.go b/android/packaging.go
index d216c0c..c2b0c1d 100644
--- a/android/packaging.go
+++ b/android/packaging.go
@@ -202,6 +202,11 @@
return p.srcPath
}
+// The symlink target of the PackagingSpec. Do not use, for the soong-only migration.
+func (p *PackagingSpec) SymlinkTarget() string {
+ return p.symlinkTarget
+}
+
type PackageModule interface {
Module
packagingBase() *PackagingBase
diff --git a/filesystem/fsverity_metadata.go b/filesystem/fsverity_metadata.go
index a3a2086..478fea9 100644
--- a/filesystem/fsverity_metadata.go
+++ b/filesystem/fsverity_metadata.go
@@ -21,9 +21,27 @@
"android/soong/android"
+ "github.com/google/blueprint"
"github.com/google/blueprint/proptools"
)
+func init() {
+ pctx.HostBinToolVariable("fsverity_metadata_generator", "fsverity_metadata_generator")
+ pctx.HostBinToolVariable("fsverity_manifest_generator", "fsverity_manifest_generator")
+ pctx.HostBinToolVariable("fsverity", "fsverity")
+}
+
+var (
+ buildFsverityMeta = pctx.AndroidStaticRule("build_fsverity_meta", blueprint.RuleParams{
+ Command: `$fsverity_metadata_generator --fsverity-path $fsverity --signature none --hash-alg sha256 --output $out $in`,
+ CommandDeps: []string{"$fsverity_metadata_generator", "$fsverity"},
+ })
+ buildFsverityManifest = pctx.AndroidStaticRule("build_fsverity_manifest", blueprint.RuleParams{
+ Command: `$fsverity_manifest_generator --fsverity-path $fsverity --output $out @$in`,
+ CommandDeps: []string{"$fsverity_manifest_generator", "$fsverity"},
+ })
+)
+
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.
@@ -35,13 +53,57 @@
Libs proptools.Configurable[[]string] `android:"path"`
}
-func (f *filesystem) writeManifestGeneratorListFile(ctx android.ModuleContext, outputPath android.WritablePath, matchedSpecs []android.PackagingSpec, rebasedDir android.OutputPath) {
+// Mapping of a given fsverity file, which may be a real file or a symlink, and the on-device
+// path it should have relative to the filesystem root.
+type fsveritySrcDest struct {
+ src android.Path
+ dest string
+}
+
+func (f *filesystem) writeManifestGeneratorListFile(
+ ctx android.ModuleContext,
+ outputPath android.WritablePath,
+ matchedFiles []fsveritySrcDest,
+ rootDir android.OutputPath,
+ rebasedDir android.OutputPath,
+) []android.Path {
+ prefix, err := filepath.Rel(rootDir.String(), rebasedDir.String())
+ if err != nil {
+ panic("rebasedDir should be relative to rootDir")
+ }
+ if prefix == "." {
+ prefix = ""
+ }
+ if f.PartitionType() == "system_ext" {
+ // Use the equivalent of $PRODUCT_OUT as the base dir.
+ // This ensures that the paths in build_manifest.pb contain on-device paths
+ // e.g. system_ext/framework/javalib.jar
+ // and not framework/javalib.jar.
+ //
+ // Although base-dir is outside the rootdir provided for packaging, this action
+ // is hermetic since it uses `manifestGeneratorListPath` to filter the files to be written to build_manifest.pb
+ prefix = "system_ext"
+ }
+
+ var deps []android.Path
var buf strings.Builder
- for _, spec := range matchedSpecs {
- buf.WriteString(rebasedDir.Join(ctx, spec.RelPathInPackage()).String())
- buf.WriteRune('\n')
+ for _, spec := range matchedFiles {
+ src := spec.src.String()
+ dst := filepath.Join(prefix, spec.dest)
+ if strings.Contains(src, ",") {
+ ctx.ModuleErrorf("Path cannot contain a comma: %s", src)
+ }
+ if strings.Contains(dst, ",") {
+ ctx.ModuleErrorf("Path cannot contain a comma: %s", dst)
+ }
+ buf.WriteString(src)
+ buf.WriteString(",")
+ buf.WriteString(dst)
+ buf.WriteString("\n")
+ deps = append(deps, spec.src)
}
android.WriteFileRuleVerbatim(ctx, outputPath, buf.String())
+ return deps
}
func (f *filesystem) buildFsverityMetadataFiles(
@@ -64,69 +126,98 @@
return false
}
- var matchedSpecs []android.PackagingSpec
+ var matchedFiles []android.PackagingSpec
+ var matchedSymlinks []android.PackagingSpec
for _, relPath := range android.SortedKeys(specs) {
if match(relPath) {
- matchedSpecs = append(matchedSpecs, specs[relPath])
+ spec := specs[relPath]
+ if spec.SrcPath() != nil {
+ matchedFiles = append(matchedFiles, spec)
+ } else if spec.SymlinkTarget() != "" {
+ matchedSymlinks = append(matchedSymlinks, spec)
+ } else {
+ ctx.ModuleErrorf("Expected a file or symlink for fsverity packaging spec")
+ }
}
}
- if len(matchedSpecs) == 0 {
+ if len(matchedFiles) == 0 && len(matchedSymlinks) == 0 {
return
}
- fsverityPath := ctx.Config().HostToolPath(ctx, "fsverity")
-
// STEP 1: generate .fsv_meta
- var sb strings.Builder
- sb.WriteString("set -e\n")
- for _, spec := range matchedSpecs {
+ var fsverityFileSpecs []fsveritySrcDest
+ for _, spec := range matchedFiles {
+ rel := spec.RelPathInPackage() + ".fsv_meta"
+ outPath := android.PathForModuleOut(ctx, "fsverity/meta_files", rel)
+ destPath := rebasedDir.Join(ctx, rel)
// srcPath is copied by CopySpecsToDir()
- srcPath := rebasedDir.Join(ctx, spec.RelPathInPackage())
- destPath := rebasedDir.Join(ctx, spec.RelPathInPackage()+".fsv_meta")
- builder.Command().
- BuiltTool("fsverity_metadata_generator").
- FlagWithInput("--fsverity-path ", fsverityPath).
- FlagWithArg("--signature ", "none").
- FlagWithArg("--hash-alg ", "sha256").
- FlagWithOutput("--output ", destPath).
- Text(srcPath.String())
+ ctx.Build(pctx, android.BuildParams{
+ Rule: buildFsverityMeta,
+ Input: spec.SrcPath(),
+ Output: outPath,
+ })
+ builder.Command().Textf("cp").Input(outPath).Output(destPath)
f.appendToEntry(ctx, destPath)
*fullInstallPaths = append(*fullInstallPaths, FullInstallPathInfo{
SourcePath: destPath,
- FullInstallPath: android.PathForModuleInPartitionInstall(ctx, f.PartitionType(), spec.RelPathInPackage()+".fsv_meta"),
+ FullInstallPath: android.PathForModuleInPartitionInstall(ctx, f.PartitionType(), rel),
+ })
+ fsverityFileSpecs = append(fsverityFileSpecs, fsveritySrcDest{
+ src: spec.SrcPath(),
+ dest: spec.RelPathInPackage(),
})
}
-
- fsVerityBaseDir := rootDir.String()
- if f.PartitionType() == "system_ext" {
- // Use the equivalent of $PRODUCT_OUT as the base dir.
- // This ensures that the paths in build_manifest.pb contain on-device paths
- // e.g. system_ext/framework/javalib.jar
- // and not framework/javalib.jar.
- //
- // Although base-dir is outside the rootdir provided for packaging, this action
- // is hermetic since it uses `manifestGeneratorListPath` to filter the files to be written to build_manifest.pb
- fsVerityBaseDir = filepath.Dir(rootDir.String())
+ for _, spec := range matchedSymlinks {
+ rel := spec.RelPathInPackage() + ".fsv_meta"
+ outPath := android.PathForModuleOut(ctx, "fsverity/meta_files", rel)
+ destPath := rebasedDir.Join(ctx, rel)
+ target := spec.SymlinkTarget() + ".fsv_meta"
+ ctx.Build(pctx, android.BuildParams{
+ Rule: android.Symlink,
+ Output: outPath,
+ Args: map[string]string{
+ "fromPath": target,
+ },
+ })
+ builder.Command().
+ Textf("cp").
+ Flag(ctx.Config().CpPreserveSymlinksFlags()).
+ Input(outPath).
+ Output(destPath)
+ f.appendToEntry(ctx, destPath)
+ *fullInstallPaths = append(*fullInstallPaths, FullInstallPathInfo{
+ SymlinkTarget: target,
+ FullInstallPath: android.PathForModuleInPartitionInstall(ctx, f.PartitionType(), rel),
+ })
+ // The fsverity manifest tool needs to actually look at the symlink. But symlink
+ // packagingSpecs are not actually created on disk, at least until the staging dir is
+ // built for the partition. Create a fake one now so the tool can see it.
+ realizedSymlink := android.PathForModuleOut(ctx, "fsverity/realized_symlinks", spec.RelPathInPackage())
+ ctx.Build(pctx, android.BuildParams{
+ Rule: android.Symlink,
+ Output: realizedSymlink,
+ Args: map[string]string{
+ "fromPath": spec.SymlinkTarget(),
+ },
+ })
+ fsverityFileSpecs = append(fsverityFileSpecs, fsveritySrcDest{
+ src: realizedSymlink,
+ dest: spec.RelPathInPackage(),
+ })
}
// STEP 2: generate signed BuildManifest.apk
// STEP 2-1: generate build_manifest.pb
- manifestGeneratorListPath := android.PathForModuleOut(ctx, "fsverity_manifest.list")
- f.writeManifestGeneratorListFile(ctx, manifestGeneratorListPath, matchedSpecs, rebasedDir)
- assetsPath := android.PathForModuleOut(ctx, "fsverity_manifest/assets")
- manifestPbPath := assetsPath.Join(ctx, "build_manifest.pb")
- builder.Command().Text("rm -rf " + assetsPath.String())
- builder.Command().Text("mkdir -p " + assetsPath.String())
- builder.Command().
- BuiltTool("fsverity_manifest_generator").
- FlagWithInput("--fsverity-path ", fsverityPath).
- FlagWithArg("--base-dir ", fsVerityBaseDir).
- FlagWithArg("--output ", manifestPbPath.String()).
- FlagWithInput("@", manifestGeneratorListPath)
-
- f.appendToEntry(ctx, manifestPbPath)
- f.appendToEntry(ctx, manifestGeneratorListPath)
+ manifestGeneratorListPath := android.PathForModuleOut(ctx, "fsverity/fsverity_manifest.list")
+ manifestDeps := f.writeManifestGeneratorListFile(ctx, manifestGeneratorListPath, fsverityFileSpecs, rootDir, rebasedDir)
+ manifestPbPath := android.PathForModuleOut(ctx, "fsverity/build_manifest.pb")
+ ctx.Build(pctx, android.BuildParams{
+ Rule: buildFsverityManifest,
+ Input: manifestGeneratorListPath,
+ Implicits: manifestDeps,
+ Output: manifestPbPath,
+ })
// STEP 2-2: generate BuildManifest.apk (unsigned)
apkNameSuffix := ""
@@ -134,8 +225,8 @@
//https://source.corp.google.com/h/googleplex-android/platform/build/+/e392d2b486c2d4187b20a72b1c67cc737ecbcca5:core/Makefile;l=3410;drc=ea8f34bc1d6e63656b4ec32f2391e9d54b3ebb6b;bpv=1;bpt=0
apkNameSuffix = "SystemExt"
}
- apkPath := rebasedDir.Join(ctx, "etc", "security", "fsverity", fmt.Sprintf("BuildManifest%s.apk", apkNameSuffix))
- idsigPath := rebasedDir.Join(ctx, "etc", "security", "fsverity", fmt.Sprintf("BuildManifest%s.apk.idsig", apkNameSuffix))
+ apkPath := android.PathForModuleOut(ctx, "fsverity", fmt.Sprintf("BuildManifest%s.apk", apkNameSuffix))
+ idsigPath := android.PathForModuleOut(ctx, "fsverity", fmt.Sprintf("BuildManifest%s.apk.idsig", apkNameSuffix))
manifestTemplatePath := android.PathForSource(ctx, "system/security/fsverity/AndroidManifest.xml")
libs := android.PathsForModuleSrc(ctx, f.properties.Fsverity.Libs.GetOrDefault(ctx, nil))
@@ -144,12 +235,23 @@
minSdkVersion = ctx.Config().PlatformSdkVersion().String()
}
- unsignedApkCommand := builder.Command().
- Textf("mkdir -p %s && ", filepath.Dir(apkPath.String())).
+ apkBuilder := android.NewRuleBuilder(pctx, ctx)
+
+ // aapt2 doesn't support adding individual asset files. Create a temp directory to hold asset
+ // files and pass it to aapt2.
+ tmpAssetDir := android.PathForModuleOut(ctx, "fsverity/tmp_asset_dir")
+ stagedManifestPbPath := tmpAssetDir.Join(ctx, "build_manifest.pb")
+ apkBuilder.Command().
+ Text("rm -rf").Text(tmpAssetDir.String()).
+ Text("&&").
+ Text("mkdir -p").Text(tmpAssetDir.String())
+ apkBuilder.Command().Text("cp").Input(manifestPbPath).Output(stagedManifestPbPath)
+
+ unsignedApkCommand := apkBuilder.Command().
BuiltTool("aapt2").
Text("link").
FlagWithOutput("-o ", apkPath).
- FlagWithArg("-A ", assetsPath.String())
+ FlagWithArg("-A ", tmpAssetDir.String()).Implicit(stagedManifestPbPath)
for _, lib := range libs {
unsignedApkCommand.FlagWithInput("-I ", lib)
}
@@ -159,26 +261,35 @@
FlagWithArg("--version-name ", ctx.Config().AppsDefaultVersionName()).
FlagWithInput("--manifest ", manifestTemplatePath).
Text(" --rename-manifest-package com.android.security.fsverity_metadata." + f.partitionName())
- *fullInstallPaths = append(*fullInstallPaths, FullInstallPathInfo{
- SourcePath: apkPath,
- FullInstallPath: android.PathForModuleInPartitionInstall(ctx, f.PartitionType(), fmt.Sprintf("etc/security/fsverity/BuildManifest%s.apk", apkNameSuffix)),
- })
-
- f.appendToEntry(ctx, apkPath)
// STEP 2-3: sign BuildManifest.apk
pemPath, keyPath := ctx.Config().DefaultAppCertificate(ctx)
- builder.Command().
+ apkBuilder.Command().
BuiltTool("apksigner").
Text("sign").
FlagWithArg("--in ", apkPath.String()).
FlagWithInput("--cert ", pemPath).
FlagWithInput("--key ", keyPath).
ImplicitOutput(idsigPath)
+ apkBuilder.Build(fmt.Sprintf("%s_fsverity_apk", ctx.ModuleName()), "build fsverity apk")
+
+ // STEP 2-4: Install the apk into the staging directory
+ installedApkPath := rebasedDir.Join(ctx, "etc", "security", "fsverity", fmt.Sprintf("BuildManifest%s.apk", apkNameSuffix))
+ installedIdsigPath := rebasedDir.Join(ctx, "etc", "security", "fsverity", fmt.Sprintf("BuildManifest%s.apk.idsig", apkNameSuffix))
+ builder.Command().Text("cp").Input(apkPath).Text(installedApkPath.String())
+ builder.Command().Text("cp").Input(idsigPath).Text(installedIdsigPath.String())
+
+ *fullInstallPaths = append(*fullInstallPaths, FullInstallPathInfo{
+ SourcePath: apkPath,
+ FullInstallPath: android.PathForModuleInPartitionInstall(ctx, f.PartitionType(), fmt.Sprintf("etc/security/fsverity/BuildManifest%s.apk", apkNameSuffix)),
+ })
+
+ f.appendToEntry(ctx, installedApkPath)
+
*fullInstallPaths = append(*fullInstallPaths, FullInstallPathInfo{
SourcePath: idsigPath,
FullInstallPath: android.PathForModuleInPartitionInstall(ctx, f.PartitionType(), fmt.Sprintf("etc/security/fsverity/BuildManifest%s.apk.idsig", apkNameSuffix)),
})
- f.appendToEntry(ctx, idsigPath)
+ f.appendToEntry(ctx, installedIdsigPath)
}