diff options
-rw-r--r-- | android/packaging.go | 5 | ||||
-rw-r--r-- | filesystem/fsverity_metadata.go | 234 |
2 files changed, 178 insertions, 61 deletions
diff --git a/android/packaging.go b/android/packaging.go index 551fd4c3f..4e0c74a12 100644 --- a/android/packaging.go +++ b/android/packaging.go @@ -202,6 +202,11 @@ func (p *PackagingSpec) SrcPath() Path { 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 a3a2086ce..89da3182a 100644 --- a/filesystem/fsverity_metadata.go +++ b/filesystem/fsverity_metadata.go @@ -21,9 +21,27 @@ import ( "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 @@ type fsverityProperties struct { 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 @@ func (f *filesystem) buildFsverityMetadataFiles( 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 @@ func (f *filesystem) buildFsverityMetadataFiles( //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 @@ func (f *filesystem) buildFsverityMetadataFiles( 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,36 @@ func (f *filesystem) buildFsverityMetadataFiles( 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("mkdir -p").Text(filepath.Dir(installedApkPath.String())) + 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) } |