summaryrefslogtreecommitdiff
path: root/filesystem
diff options
context:
space:
mode:
Diffstat (limited to 'filesystem')
-rw-r--r--filesystem/Android.bp5
-rw-r--r--filesystem/aconfig_files.go84
-rw-r--r--filesystem/avb_add_hash_footer.go29
-rw-r--r--filesystem/avb_gen_vbmeta_image.go25
-rw-r--r--filesystem/bootimg.go12
-rw-r--r--filesystem/filesystem.go385
-rw-r--r--filesystem/filesystem_test.go356
-rw-r--r--filesystem/fsverity_metadata.go179
-rw-r--r--filesystem/logical_partition.go12
-rw-r--r--filesystem/raw_binary.go14
-rw-r--r--filesystem/system_image.go45
-rw-r--r--filesystem/vbmeta.go37
12 files changed, 981 insertions, 202 deletions
diff --git a/filesystem/Android.bp b/filesystem/Android.bp
index 07d57c915..a08f7cf17 100644
--- a/filesystem/Android.bp
+++ b/filesystem/Android.bp
@@ -9,13 +9,18 @@ bootstrap_go_package {
"blueprint",
"soong",
"soong-android",
+ "soong-bpf", // for testing
+ "soong-java", // for testing
"soong-linkerconfig",
+ "soong-phony", // for testing
],
srcs: [
+ "aconfig_files.go",
"avb_add_hash_footer.go",
"avb_gen_vbmeta_image.go",
"bootimg.go",
"filesystem.go",
+ "fsverity_metadata.go",
"logical_partition.go",
"raw_binary.go",
"system_image.go",
diff --git a/filesystem/aconfig_files.go b/filesystem/aconfig_files.go
new file mode 100644
index 000000000..5c047bc83
--- /dev/null
+++ b/filesystem/aconfig_files.go
@@ -0,0 +1,84 @@
+// 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 (
+ "android/soong/android"
+ "strings"
+
+ "github.com/google/blueprint/proptools"
+)
+
+func (f *filesystem) buildAconfigFlagsFiles(ctx android.ModuleContext, builder *android.RuleBuilder, specs map[string]android.PackagingSpec, dir android.OutputPath) {
+ if !proptools.Bool(f.properties.Gen_aconfig_flags_pb) {
+ return
+ }
+
+ aconfigFlagsBuilderPath := android.PathForModuleOut(ctx, "aconfig_flags_builder.sh")
+ aconfigToolPath := ctx.Config().HostToolPath(ctx, "aconfig")
+ cmd := builder.Command().Tool(aconfigFlagsBuilderPath).Implicit(aconfigToolPath)
+
+ var caches []string
+ for _, ps := range specs {
+ cmd.Implicits(ps.GetAconfigPaths())
+ caches = append(caches, ps.GetAconfigPaths().Strings()...)
+ }
+ caches = android.SortedUniqueStrings(caches)
+
+ var sbCaches strings.Builder
+ for _, cache := range caches {
+ sbCaches.WriteString(" --cache ")
+ sbCaches.WriteString(cache)
+ sbCaches.WriteString(" \\\n")
+ }
+ sbCaches.WriteRune('\n')
+
+ var sb strings.Builder
+ sb.WriteString("set -e\n")
+
+ installAconfigFlagsPath := dir.Join(ctx, "etc", "aconfig_flags.pb")
+ sb.WriteString(aconfigToolPath.String())
+ sb.WriteString(" dump-cache --dedup --format protobuf --out ")
+ sb.WriteString(installAconfigFlagsPath.String())
+ sb.WriteString(" \\\n")
+ sb.WriteString(sbCaches.String())
+ cmd.ImplicitOutput(installAconfigFlagsPath)
+ f.appendToEntry(ctx, installAconfigFlagsPath)
+
+ installAconfigStorageDir := dir.Join(ctx, "etc", "aconfig")
+ sb.WriteString("mkdir -p ")
+ sb.WriteString(installAconfigStorageDir.String())
+ sb.WriteRune('\n')
+
+ generatePartitionAconfigStorageFile := func(fileType, fileName string) {
+ outputPath := installAconfigStorageDir.Join(ctx, fileName)
+ sb.WriteString(aconfigToolPath.String())
+ sb.WriteString(" create-storage --container ")
+ sb.WriteString(f.PartitionType())
+ sb.WriteString(" --file ")
+ sb.WriteString(fileType)
+ sb.WriteString(" --out ")
+ sb.WriteString(outputPath.String())
+ sb.WriteString(" \\\n")
+ sb.WriteString(sbCaches.String())
+ cmd.ImplicitOutput(outputPath)
+ f.appendToEntry(ctx, outputPath)
+ }
+ generatePartitionAconfigStorageFile("package_map", "package.map")
+ generatePartitionAconfigStorageFile("flag_map", "flag.map")
+ generatePartitionAconfigStorageFile("flag_val", "flag.val")
+
+ android.WriteExecutableFileRuleVerbatim(ctx, aconfigFlagsBuilderPath, sb.String())
+}
diff --git a/filesystem/avb_add_hash_footer.go b/filesystem/avb_add_hash_footer.go
index f3fecd042..469f1fb0a 100644
--- a/filesystem/avb_add_hash_footer.go
+++ b/filesystem/avb_add_hash_footer.go
@@ -25,6 +25,7 @@ import (
type avbAddHashFooter struct {
android.ModuleBase
+ android.DefaultableModuleBase
properties avbAddHashFooterProperties
@@ -68,6 +69,9 @@ type avbAddHashFooterProperties struct {
// List of properties to add to the footer
Props []avbProp
+ // The index used to prevent rollback of the image on device.
+ Rollback_index *int64
+
// Include descriptors from images
Include_descriptors_from_images []string `android:"path,arch_variant"`
}
@@ -77,6 +81,7 @@ func avbAddHashFooterFactory() android.Module {
module := &avbAddHashFooter{}
module.AddProperties(&module.properties)
android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibFirst)
+ android.InitDefaultableModule(module)
return module
}
@@ -128,6 +133,14 @@ func (a *avbAddHashFooter) GenerateAndroidBuildActions(ctx android.ModuleContext
addAvbProp(ctx, cmd, prop)
}
+ if a.properties.Rollback_index != nil {
+ rollbackIndex := proptools.Int(a.properties.Rollback_index)
+ if rollbackIndex < 0 {
+ ctx.PropertyErrorf("rollback_index", "Rollback index must be non-negative")
+ }
+ cmd.Flag(fmt.Sprintf(" --rollback_index %d", rollbackIndex))
+ }
+
cmd.FlagWithOutput("--image ", a.output)
builder.Build("avbAddHashFooter", fmt.Sprintf("avbAddHashFooter %s", ctx.ModuleName()))
@@ -195,3 +208,19 @@ var _ android.SourceFileProducer = (*avbAddHashFooter)(nil)
func (a *avbAddHashFooter) Srcs() android.Paths {
return append(android.Paths{}, a.output)
}
+
+type avbAddHashFooterDefaults struct {
+ android.ModuleBase
+ android.DefaultsModuleBase
+}
+
+// avb_add_hash_footer_defaults provides a set of properties that can be inherited by other
+// avb_add_hash_footer modules. A module can use the properties from an avb_add_hash_footer_defaults
+// using `defaults: ["<:default_module_name>"]`. Properties of both modules are erged (when
+// possible) by prepending the default module's values to the depending module's values.
+func avbAddHashFooterDefaultsFactory() android.Module {
+ module := &avbAddHashFooterDefaults{}
+ module.AddProperties(&avbAddHashFooterProperties{})
+ android.InitDefaultsModule(module)
+ return module
+}
diff --git a/filesystem/avb_gen_vbmeta_image.go b/filesystem/avb_gen_vbmeta_image.go
index 0f331f923..a7fd7829e 100644
--- a/filesystem/avb_gen_vbmeta_image.go
+++ b/filesystem/avb_gen_vbmeta_image.go
@@ -24,6 +24,7 @@ import (
type avbGenVbmetaImage struct {
android.ModuleBase
+ android.DefaultableModuleBase
properties avbGenVbmetaImageProperties
@@ -47,6 +48,7 @@ func avbGenVbmetaImageFactory() android.Module {
module := &avbGenVbmetaImage{}
module.AddProperties(&module.properties)
android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibFirst)
+ android.InitDefaultableModule(module)
return module
}
@@ -79,6 +81,8 @@ func (a *avbGenVbmetaImage) GenerateAndroidBuildActions(ctx android.ModuleContex
a.output = android.PathForModuleOut(ctx, a.installFileName()).OutputPath
cmd.FlagWithOutput("--output_vbmeta_image ", a.output)
builder.Build("avbGenVbmetaImage", fmt.Sprintf("avbGenVbmetaImage %s", ctx.ModuleName()))
+
+ ctx.SetOutputFiles([]android.Path{a.output}, "")
}
var _ android.AndroidMkEntriesProvider = (*avbGenVbmetaImage)(nil)
@@ -97,12 +101,19 @@ func (a *avbGenVbmetaImage) AndroidMkEntries() []android.AndroidMkEntries {
}}
}
-var _ android.OutputFileProducer = (*avbGenVbmetaImage)(nil)
+type avbGenVbmetaImageDefaults struct {
+ android.ModuleBase
+ android.DefaultsModuleBase
+}
-// Implements android.OutputFileProducer
-func (a *avbGenVbmetaImage) OutputFiles(tag string) (android.Paths, error) {
- if tag == "" {
- return []android.Path{a.output}, nil
- }
- return nil, fmt.Errorf("unsupported module reference tag %q", tag)
+// avb_gen_vbmeta_image_defaults provides a set of properties that can be inherited by other
+// avb_gen_vbmeta_image modules. A module can use the properties from an
+// avb_gen_vbmeta_image_defaults using `defaults: ["<:default_module_name>"]`. Properties of both
+// modules are erged (when possible) by prepending the default module's values to the depending
+// module's values.
+func avbGenVbmetaImageDefaultsFactory() android.Module {
+ module := &avbGenVbmetaImageDefaults{}
+ module.AddProperties(&avbGenVbmetaImageProperties{})
+ android.InitDefaultsModule(module)
+ return module
}
diff --git a/filesystem/bootimg.go b/filesystem/bootimg.go
index 352b45178..e796ab9b3 100644
--- a/filesystem/bootimg.go
+++ b/filesystem/bootimg.go
@@ -123,6 +123,8 @@ func (b *bootimg) GenerateAndroidBuildActions(ctx android.ModuleContext) {
b.installDir = android.PathForModuleInstall(ctx, "etc")
ctx.InstallFile(b.installDir, b.installFileName(), b.output)
+
+ ctx.SetOutputFiles([]android.Path{b.output}, "")
}
func (b *bootimg) buildBootImage(ctx android.ModuleContext, vendor bool) android.OutputPath {
@@ -292,13 +294,3 @@ func (b *bootimg) SignedOutputPath() android.Path {
}
return nil
}
-
-var _ android.OutputFileProducer = (*bootimg)(nil)
-
-// Implements android.OutputFileProducer
-func (b *bootimg) OutputFiles(tag string) (android.Paths, error) {
- if tag == "" {
- return []android.Path{b.output}, nil
- }
- return nil, fmt.Errorf("unsupported module reference tag %q", tag)
-}
diff --git a/filesystem/filesystem.go b/filesystem/filesystem.go
index 023c69adf..5c7ef434f 100644
--- a/filesystem/filesystem.go
+++ b/filesystem/filesystem.go
@@ -19,6 +19,8 @@ import (
"fmt"
"io"
"path/filepath"
+ "slices"
+ "strconv"
"strings"
"android/soong/android"
@@ -34,27 +36,33 @@ func init() {
func registerBuildComponents(ctx android.RegistrationContext) {
ctx.RegisterModuleType("android_filesystem", filesystemFactory)
+ ctx.RegisterModuleType("android_filesystem_defaults", filesystemDefaultsFactory)
ctx.RegisterModuleType("android_system_image", systemImageFactory)
ctx.RegisterModuleType("avb_add_hash_footer", avbAddHashFooterFactory)
+ ctx.RegisterModuleType("avb_add_hash_footer_defaults", avbAddHashFooterDefaultsFactory)
ctx.RegisterModuleType("avb_gen_vbmeta_image", avbGenVbmetaImageFactory)
+ ctx.RegisterModuleType("avb_gen_vbmeta_image_defaults", avbGenVbmetaImageDefaultsFactory)
}
type filesystem struct {
android.ModuleBase
android.PackagingBase
+ android.DefaultableModuleBase
properties filesystemProperties
// Function that builds extra files under the root directory and returns the files
buildExtraFiles func(ctx android.ModuleContext, root android.OutputPath) android.OutputPaths
- // Function that filters PackagingSpecs returned by PackagingBase.GatherPackagingSpecs()
- filterPackagingSpecs func(specs map[string]android.PackagingSpec)
+ // Function that filters PackagingSpec in PackagingBase.GatherPackagingSpecs()
+ filterPackagingSpec func(spec android.PackagingSpec) bool
output android.OutputPath
installDir android.InstallPath
- // For testing. Keeps the result of CopyDepsToZip()
+ fileListFile android.OutputPath
+
+ // Keeps the entries installed from this filesystem
entries []string
}
@@ -78,6 +86,9 @@ type filesystemProperties struct {
// avbtool. Default used by avbtool is sha1.
Avb_hash_algorithm *string
+ // The index used to prevent rollback of the image. Only used if use_avb is true.
+ Rollback_index *int64
+
// Name of the partition stored in vbmeta desc. Defaults to the name of this module.
Partition_name *string
@@ -85,6 +96,10 @@ type filesystemProperties struct {
// is ext4.
Type *string
+ // Identifies which partition this is for //visibility:any_system_image (and others) visibility
+ // checks, and will be used in the future for API surface checks.
+ Partition_type *string
+
// file_contexts file to make image. Currently, only ext4 is supported.
File_contexts *string `android:"path"`
@@ -93,7 +108,7 @@ type filesystemProperties struct {
Base_dir *string
// Directories to be created under root. e.g. /dev, /proc, etc.
- Dirs []string
+ Dirs proptools.Configurable[[]string]
// Symbolic links to be created under root with "ln -sf <target> <name>".
Symlinks []symlinkDefinition
@@ -104,6 +119,24 @@ type filesystemProperties struct {
// When set, passed to mkuserimg_mke2fs --mke2fs_uuid & --mke2fs_hash_seed.
// Otherwise, they'll be set as random which might cause indeterministic build output.
Uuid *string
+
+ // Mount point for this image. Default is "/"
+ Mount_point *string
+
+ // If set to the name of a partition ("system", "vendor", etc), this filesystem module
+ // will also include the contents of the make-built staging directories. If any soong
+ // modules would be installed to the same location as a make module, they will overwrite
+ // the make version.
+ Include_make_built_files string
+
+ // When set, builds etc/event-log-tags file by merging logtags from all dependencies.
+ // Default is false
+ Build_logtags *bool
+
+ // Install aconfig_flags.pb file for the modules installed in this partition.
+ Gen_aconfig_flags_pb *bool
+
+ Fsverity fsverityProperties
}
// android_filesystem packages a set of modules and their transitive dependencies into a filesystem
@@ -113,6 +146,7 @@ type filesystemProperties struct {
// partitions like system.img. For example, cc_library modules are placed under ./lib[64] directory.
func filesystemFactory() android.Module {
module := &filesystem{}
+ module.filterPackagingSpec = module.filterInstallablePackagingSpec
initFilesystemModule(module)
return module
}
@@ -120,7 +154,9 @@ func filesystemFactory() android.Module {
func initFilesystemModule(module *filesystem) {
module.AddProperties(&module.properties)
android.InitPackageModule(module)
+ module.PackagingBase.DepsCollectFirstTargetOnly = true
android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
+ android.InitDefaultableModule(module)
}
var dependencyTag = struct {
@@ -160,9 +196,20 @@ func (f *filesystem) installFileName() string {
return f.BaseModuleName() + ".img"
}
+func (f *filesystem) partitionName() string {
+ return proptools.StringDefault(f.properties.Partition_name, f.Name())
+}
+
+func (f *filesystem) filterInstallablePackagingSpec(ps android.PackagingSpec) bool {
+ // Filesystem module respects the installation semantic. A PackagingSpec from a module with
+ // IsSkipInstall() is skipped.
+ return !ps.SkipInstall()
+}
+
var pctx = android.NewPackageContext("android/soong/filesystem")
func (f *filesystem) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ validatePartitionType(ctx, f)
switch f.fsType(ctx) {
case ext4Type:
f.output = f.buildImageUsingBuildImage(ctx)
@@ -176,17 +223,49 @@ func (f *filesystem) GenerateAndroidBuildActions(ctx android.ModuleContext) {
f.installDir = android.PathForModuleInstall(ctx, "etc")
ctx.InstallFile(f.installDir, f.installFileName(), f.output)
+ ctx.SetOutputFiles([]android.Path{f.output}, "")
+
+ f.fileListFile = android.PathForModuleOut(ctx, "fileList").OutputPath
+ android.WriteFileRule(ctx, f.fileListFile, f.installedFilesList())
}
-// root zip will contain extra files/dirs that are not from the `deps` property.
-func (f *filesystem) buildRootZip(ctx android.ModuleContext) android.OutputPath {
- rootDir := android.PathForModuleGen(ctx, "root").OutputPath
- builder := android.NewRuleBuilder(pctx, ctx)
- builder.Command().Text("rm -rf").Text(rootDir.String())
- builder.Command().Text("mkdir -p").Text(rootDir.String())
+func (f *filesystem) appendToEntry(ctx android.ModuleContext, installedFile android.OutputPath) {
+ partitionBaseDir := android.PathForModuleOut(ctx, "root", f.partitionName()).String() + "/"
+
+ relPath, inTargetPartition := strings.CutPrefix(installedFile.String(), partitionBaseDir)
+ if inTargetPartition {
+ f.entries = append(f.entries, relPath)
+ }
+}
+
+func (f *filesystem) installedFilesList() string {
+ installedFilePaths := android.FirstUniqueStrings(f.entries)
+ slices.Sort(installedFilePaths)
+
+ return strings.Join(installedFilePaths, "\n")
+}
+
+func validatePartitionType(ctx android.ModuleContext, p partition) {
+ if !android.InList(p.PartitionType(), validPartitions) {
+ ctx.PropertyErrorf("partition_type", "partition_type must be one of %s, found: %s", validPartitions, p.PartitionType())
+ }
+
+ ctx.VisitDirectDepsWithTag(android.DefaultsDepTag, func(m android.Module) {
+ if fdm, ok := m.(*filesystemDefaults); ok {
+ if p.PartitionType() != fdm.PartitionType() {
+ ctx.PropertyErrorf("partition_type",
+ "%s doesn't match with the partition type %s of the filesystem default module %s",
+ p.PartitionType(), fdm.PartitionType(), m.Name())
+ }
+ }
+ })
+}
+// Copy extra files/dirs that are not from the `deps` property to `rootDir`, checking for conflicts with files
+// already in `rootDir`.
+func (f *filesystem) buildNonDepsFiles(ctx android.ModuleContext, builder *android.RuleBuilder, rootDir android.OutputPath) {
// create dirs and symlinks
- for _, dir := range f.properties.Dirs {
+ for _, dir := range f.properties.Dirs.GetOrDefault(ctx, nil) {
// OutputPath.Join verifies dir
builder.Command().Text("mkdir -p").Text(rootDir.Join(ctx, dir).String())
}
@@ -207,65 +286,68 @@ func (f *filesystem) buildRootZip(ctx android.ModuleContext) android.OutputPath
// OutputPath.Join verifies name. don't need to verify target.
dst := rootDir.Join(ctx, name)
-
+ builder.Command().Textf("(! [ -e %s -o -L %s ] || (echo \"%s already exists from an earlier stage of the build\" && exit 1))", dst, dst, dst)
builder.Command().Text("mkdir -p").Text(filepath.Dir(dst.String()))
builder.Command().Text("ln -sf").Text(proptools.ShellEscape(target)).Text(dst.String())
+ f.appendToEntry(ctx, dst)
}
// create extra files if there's any
- rootForExtraFiles := android.PathForModuleGen(ctx, "root-extra").OutputPath
- var extraFiles android.OutputPaths
if f.buildExtraFiles != nil {
- extraFiles = f.buildExtraFiles(ctx, rootForExtraFiles)
- for _, f := range extraFiles {
- rel, _ := filepath.Rel(rootForExtraFiles.String(), f.String())
- if strings.HasPrefix(rel, "..") {
- panic(fmt.Errorf("%q is not under %q\n", f, rootForExtraFiles))
+ rootForExtraFiles := android.PathForModuleGen(ctx, "root-extra").OutputPath
+ extraFiles := f.buildExtraFiles(ctx, rootForExtraFiles)
+ for _, extraFile := range extraFiles {
+ rel, err := filepath.Rel(rootForExtraFiles.String(), extraFile.String())
+ if err != nil || strings.HasPrefix(rel, "..") {
+ ctx.ModuleErrorf("can't make %q relative to %q", extraFile, rootForExtraFiles)
}
+ f.appendToEntry(ctx, rootDir.Join(ctx, rel))
+ }
+ if len(extraFiles) > 0 {
+ builder.Command().BuiltTool("merge_directories").
+ Implicits(extraFiles.Paths()).
+ Text(rootDir.String()).
+ Text(rootForExtraFiles.String())
}
}
+}
+
+func (f *filesystem) copyPackagingSpecs(ctx android.ModuleContext, builder *android.RuleBuilder, specs map[string]android.PackagingSpec, rootDir, rebasedDir android.WritablePath) []string {
+ rootDirSpecs := make(map[string]android.PackagingSpec)
+ rebasedDirSpecs := make(map[string]android.PackagingSpec)
- // Zip them all
- zipOut := android.PathForModuleGen(ctx, "root.zip").OutputPath
- zipCommand := builder.Command().BuiltTool("soong_zip")
- zipCommand.FlagWithOutput("-o ", zipOut).
- FlagWithArg("-C ", rootDir.String()).
- Flag("-L 0"). // no compression because this will be unzipped soon
- FlagWithArg("-D ", rootDir.String()).
- Flag("-d") // include empty directories
- if len(extraFiles) > 0 {
- zipCommand.FlagWithArg("-C ", rootForExtraFiles.String())
- for _, f := range extraFiles {
- zipCommand.FlagWithInput("-f ", f)
+ for rel, spec := range specs {
+ if spec.Partition() == "root" {
+ rootDirSpecs[rel] = spec
+ } else {
+ rebasedDirSpecs[rel] = spec
}
}
- builder.Command().Text("rm -rf").Text(rootDir.String())
+ dirsToSpecs := make(map[android.WritablePath]map[string]android.PackagingSpec)
+ dirsToSpecs[rootDir] = rootDirSpecs
+ dirsToSpecs[rebasedDir] = rebasedDirSpecs
- builder.Build("zip_root", fmt.Sprintf("zipping root contents for %s", ctx.ModuleName()))
- return zipOut
+ return f.CopySpecsToDirs(ctx, builder, dirsToSpecs)
}
func (f *filesystem) buildImageUsingBuildImage(ctx android.ModuleContext) android.OutputPath {
- depsZipFile := android.PathForModuleOut(ctx, "deps.zip").OutputPath
- f.entries = f.CopyDepsToZip(ctx, f.gatherFilteredPackagingSpecs(ctx), depsZipFile)
-
+ rootDir := android.PathForModuleOut(ctx, "root").OutputPath
+ rebasedDir := rootDir
+ if f.properties.Base_dir != nil {
+ rebasedDir = rootDir.Join(ctx, *f.properties.Base_dir)
+ }
builder := android.NewRuleBuilder(pctx, ctx)
- depsBase := proptools.StringDefault(f.properties.Base_dir, ".")
- rebasedDepsZip := android.PathForModuleOut(ctx, "rebased_deps.zip").OutputPath
- builder.Command().
- BuiltTool("zip2zip").
- FlagWithInput("-i ", depsZipFile).
- FlagWithOutput("-o ", rebasedDepsZip).
- Text("**/*:" + proptools.ShellEscape(depsBase)) // zip2zip verifies depsBase
+ // Wipe the root dir to get rid of leftover files from prior builds
+ builder.Command().Textf("rm -rf %s && mkdir -p %s", rootDir, rootDir)
+ specs := f.gatherFilteredPackagingSpecs(ctx)
+ f.entries = f.copyPackagingSpecs(ctx, builder, specs, rootDir, rebasedDir)
- rootDir := android.PathForModuleOut(ctx, "root").OutputPath
- rootZip := f.buildRootZip(ctx)
- builder.Command().
- BuiltTool("zipsync").
- FlagWithArg("-d ", rootDir.String()). // zipsync wipes this. No need to clear.
- Input(rootZip).
- Input(rebasedDepsZip)
+ f.buildNonDepsFiles(ctx, builder, rootDir)
+ f.addMakeBuiltFiles(ctx, builder, rootDir)
+ f.buildFsverityMetadataFiles(ctx, builder, specs, rootDir, rebasedDir)
+ f.buildEventLogtagsFile(ctx, builder, rebasedDir)
+ f.buildAconfigFlagsFiles(ctx, builder, specs, rebasedDir)
// run host_init_verifier
// Ideally we should have a concept of pluggable linters that verify the generated image.
@@ -306,18 +388,16 @@ func (f *filesystem) salt() string {
}
func (f *filesystem) buildPropFile(ctx android.ModuleContext) (propFile android.OutputPath, toolDeps android.Paths) {
- type prop struct {
- name string
- value string
- }
-
- var props []prop
var deps android.Paths
+ var propFileString strings.Builder
addStr := func(name string, value string) {
- props = append(props, prop{name, value})
+ propFileString.WriteString(name)
+ propFileString.WriteRune('=')
+ propFileString.WriteString(value)
+ propFileString.WriteRune('\n')
}
addPath := func(name string, path android.Path) {
- props = append(props, prop{name, path.String()})
+ addStr(name, path.String())
deps = append(deps, path)
}
@@ -332,7 +412,7 @@ func (f *filesystem) buildPropFile(ctx android.ModuleContext) (propFile android.
}
addStr("fs_type", fsTypeStr(f.fsType(ctx)))
- addStr("mount_point", "/")
+ addStr("mount_point", proptools.StringDefault(f.properties.Mount_point, "/"))
addStr("use_dynamic_partition_size", "true")
addPath("ext_mkuserimg", ctx.Config().HostToolPath(ctx, "mkuserimg_mke2fs"))
// b/177813163 deps of the host tools have to be added. Remove this.
@@ -347,13 +427,22 @@ 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)
+ 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
}
+ if f.properties.Rollback_index != nil {
+ rollbackIndex := proptools.Int(f.properties.Rollback_index)
+ if rollbackIndex < 0 {
+ ctx.PropertyErrorf("rollback_index", "Rollback index must be non-negative")
+ }
+ avb_add_hashtree_footer_args += " --rollback_index " + strconv.Itoa(rollbackIndex)
+ }
+ 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)
- partitionName := proptools.StringDefault(f.properties.Partition_name, f.Name())
- addStr("partition_name", partitionName)
addStr("avb_salt", f.salt())
}
@@ -368,15 +457,7 @@ func (f *filesystem) buildPropFile(ctx android.ModuleContext) (propFile android.
addStr("hash_seed", uuid)
}
propFile = android.PathForModuleOut(ctx, "prop").OutputPath
- builder := android.NewRuleBuilder(pctx, ctx)
- builder.Command().Text("rm").Flag("-rf").Output(propFile)
- for _, p := range props {
- builder.Command().
- Text("echo").
- Flag(`"` + p.name + "=" + p.value + `"`).
- Text(">>").Output(propFile)
- }
- builder.Build("build_filesystem_prop", fmt.Sprintf("Creating filesystem props for %s", f.BaseModuleName()))
+ android.WriteFileRuleVerbatim(ctx, propFile, propFileString.String())
return propFile, deps
}
@@ -390,25 +471,25 @@ func (f *filesystem) buildCpioImage(ctx android.ModuleContext, compressed bool)
ctx.PropertyErrorf("file_contexts", "file_contexts is not supported for compressed cpio image.")
}
- depsZipFile := android.PathForModuleOut(ctx, "deps.zip").OutputPath
- f.entries = f.CopyDepsToZip(ctx, f.gatherFilteredPackagingSpecs(ctx), depsZipFile)
+ if f.properties.Include_make_built_files != "" {
+ ctx.PropertyErrorf("include_make_built_files", "include_make_built_files is not supported for compressed cpio image.")
+ }
+ rootDir := android.PathForModuleOut(ctx, "root").OutputPath
+ rebasedDir := rootDir
+ if f.properties.Base_dir != nil {
+ rebasedDir = rootDir.Join(ctx, *f.properties.Base_dir)
+ }
builder := android.NewRuleBuilder(pctx, ctx)
- depsBase := proptools.StringDefault(f.properties.Base_dir, ".")
- rebasedDepsZip := android.PathForModuleOut(ctx, "rebased_deps.zip").OutputPath
- builder.Command().
- BuiltTool("zip2zip").
- FlagWithInput("-i ", depsZipFile).
- FlagWithOutput("-o ", rebasedDepsZip).
- Text("**/*:" + proptools.ShellEscape(depsBase)) // zip2zip verifies depsBase
+ // Wipe the root dir to get rid of leftover files from prior builds
+ builder.Command().Textf("rm -rf %s && mkdir -p %s", rootDir, rootDir)
+ specs := f.gatherFilteredPackagingSpecs(ctx)
+ f.entries = f.copyPackagingSpecs(ctx, builder, specs, rootDir, rebasedDir)
- rootDir := android.PathForModuleOut(ctx, "root").OutputPath
- rootZip := f.buildRootZip(ctx)
- builder.Command().
- BuiltTool("zipsync").
- FlagWithArg("-d ", rootDir.String()). // zipsync wipes this. No need to clear.
- Input(rootZip).
- Input(rebasedDepsZip)
+ f.buildNonDepsFiles(ctx, builder, rootDir)
+ f.buildFsverityMetadataFiles(ctx, builder, specs, rootDir, rebasedDir)
+ f.buildEventLogtagsFile(ctx, builder, rebasedDir)
+ f.buildAconfigFlagsFiles(ctx, builder, specs, rebasedDir)
output := android.PathForModuleOut(ctx, f.installFileName()).OutputPath
cmd := builder.Command().
@@ -431,6 +512,84 @@ func (f *filesystem) buildCpioImage(ctx android.ModuleContext, compressed bool)
return output
}
+var validPartitions = []string{
+ "system",
+ "userdata",
+ "cache",
+ "system_other",
+ "vendor",
+ "product",
+ "system_ext",
+ "odm",
+ "vendor_dlkm",
+ "odm_dlkm",
+ "system_dlkm",
+}
+
+func (f *filesystem) addMakeBuiltFiles(ctx android.ModuleContext, builder *android.RuleBuilder, rootDir android.Path) {
+ partition := f.properties.Include_make_built_files
+ if partition == "" {
+ return
+ }
+ if !slices.Contains(validPartitions, partition) {
+ ctx.PropertyErrorf("include_make_built_files", "Expected one of %#v, found %q", validPartitions, partition)
+ return
+ }
+ stampFile := fmt.Sprintf("target/product/%s/obj/PACKAGING/%s_intermediates/staging_dir.stamp", ctx.Config().DeviceName(), partition)
+ fileListFile := fmt.Sprintf("target/product/%s/obj/PACKAGING/%s_intermediates/file_list.txt", ctx.Config().DeviceName(), partition)
+ stagingDir := fmt.Sprintf("target/product/%s/%s", ctx.Config().DeviceName(), partition)
+
+ builder.Command().BuiltTool("merge_directories").
+ Implicit(android.PathForArbitraryOutput(ctx, stampFile)).
+ Text("--ignore-duplicates").
+ FlagWithInput("--file-list", android.PathForArbitraryOutput(ctx, fileListFile)).
+ Text(rootDir.String()).
+ Text(android.PathForArbitraryOutput(ctx, stagingDir).String())
+}
+
+func (f *filesystem) buildEventLogtagsFile(ctx android.ModuleContext, builder *android.RuleBuilder, rebasedDir android.OutputPath) {
+ if !proptools.Bool(f.properties.Build_logtags) {
+ return
+ }
+
+ logtagsFilePaths := make(map[string]bool)
+ ctx.WalkDeps(func(child, parent android.Module) bool {
+ if logtagsInfo, ok := android.OtherModuleProvider(ctx, child, android.LogtagsProviderKey); ok {
+ for _, path := range logtagsInfo.Logtags {
+ logtagsFilePaths[path.String()] = true
+ }
+ }
+ return true
+ })
+
+ if len(logtagsFilePaths) == 0 {
+ return
+ }
+
+ etcPath := rebasedDir.Join(ctx, "etc")
+ eventLogtagsPath := etcPath.Join(ctx, "event-log-tags")
+ builder.Command().Text("mkdir").Flag("-p").Text(etcPath.String())
+ cmd := builder.Command().BuiltTool("merge-event-log-tags").
+ FlagWithArg("-o ", eventLogtagsPath.String()).
+ FlagWithInput("-m ", android.MergedLogtagsPath(ctx))
+
+ for _, path := range android.SortedKeys(logtagsFilePaths) {
+ cmd.Text(path)
+ }
+
+ f.appendToEntry(ctx, eventLogtagsPath)
+}
+
+type partition interface {
+ PartitionType() string
+}
+
+func (f *filesystem) PartitionType() string {
+ return proptools.StringDefault(f.properties.Partition_type, "system")
+}
+
+var _ partition = (*filesystem)(nil)
+
var _ android.AndroidMkEntriesProvider = (*filesystem)(nil)
// Implements android.AndroidMkEntriesProvider
@@ -442,21 +601,12 @@ func (f *filesystem) AndroidMkEntries() []android.AndroidMkEntries {
func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
entries.SetString("LOCAL_MODULE_PATH", f.installDir.String())
entries.SetString("LOCAL_INSTALLED_MODULE_STEM", f.installFileName())
+ entries.SetString("LOCAL_FILESYSTEM_FILELIST", f.fileListFile.String())
},
},
}}
}
-var _ android.OutputFileProducer = (*filesystem)(nil)
-
-// Implements android.OutputFileProducer
-func (f *filesystem) OutputFiles(tag string) (android.Paths, error) {
- if tag == "" {
- return []android.Path{f.output}, nil
- }
- return nil, fmt.Errorf("unsupported module reference tag %q", tag)
-}
-
// Filesystem is the public interface for the filesystem struct. Currently, it's only for the apex
// package to have access to the output file.
type Filesystem interface {
@@ -485,10 +635,7 @@ func (f *filesystem) SignedOutputPath() android.Path {
// Note that "apex" module installs its contents to "apex"(fake partition) as well
// for symbol lookup by imitating "activated" paths.
func (f *filesystem) gatherFilteredPackagingSpecs(ctx android.ModuleContext) map[string]android.PackagingSpec {
- specs := f.PackagingBase.GatherPackagingSpecs(ctx)
- if f.filterPackagingSpecs != nil {
- f.filterPackagingSpecs(specs)
- }
+ specs := f.PackagingBase.GatherPackagingSpecsWithFilter(ctx, f.filterPackagingSpec)
return specs
}
@@ -504,6 +651,40 @@ func sha1sum(values []string) string {
var _ cc.UseCoverage = (*filesystem)(nil)
-func (*filesystem) IsNativeCoverageNeeded(ctx android.BaseModuleContext) bool {
+func (*filesystem) IsNativeCoverageNeeded(ctx cc.IsNativeCoverageNeededContext) bool {
return ctx.Device() && ctx.DeviceConfig().NativeCoverageEnabled()
}
+
+// android_filesystem_defaults
+
+type filesystemDefaults struct {
+ android.ModuleBase
+ android.DefaultsModuleBase
+
+ properties filesystemDefaultsProperties
+}
+
+type filesystemDefaultsProperties struct {
+ // Identifies which partition this is for //visibility:any_system_image (and others) visibility
+ // checks, and will be used in the future for API surface checks.
+ Partition_type *string
+}
+
+// android_filesystem_defaults is a default module for android_filesystem and android_system_image
+func filesystemDefaultsFactory() android.Module {
+ module := &filesystemDefaults{}
+ module.AddProperties(&module.properties)
+ module.AddProperties(&android.PackagingProperties{})
+ android.InitDefaultsModule(module)
+ return module
+}
+
+func (f *filesystemDefaults) PartitionType() string {
+ return proptools.StringDefault(f.properties.Partition_type, "system")
+}
+
+var _ partition = (*filesystemDefaults)(nil)
+
+func (f *filesystemDefaults) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ validatePartitionType(ctx, f)
+}
diff --git a/filesystem/filesystem_test.go b/filesystem/filesystem_test.go
index aef475650..2dc8c21e0 100644
--- a/filesystem/filesystem_test.go
+++ b/filesystem/filesystem_test.go
@@ -16,11 +16,15 @@ package filesystem
import (
"os"
+ "path/filepath"
"testing"
"android/soong/android"
+ "android/soong/bpf"
"android/soong/cc"
"android/soong/etc"
+ "android/soong/java"
+ "android/soong/phony"
"github.com/google/blueprint/proptools"
)
@@ -31,8 +35,13 @@ func TestMain(m *testing.M) {
var fixture = android.GroupFixturePreparers(
android.PrepareForIntegrationTestWithAndroid,
- etc.PrepareForTestWithPrebuiltEtc,
+ android.PrepareForTestWithAndroidBuildComponents,
+ bpf.PrepareForTestWithBpf,
cc.PrepareForIntegrationTestWithCc,
+ etc.PrepareForTestWithPrebuiltEtc,
+ java.PrepareForTestWithJavaBuildComponents,
+ java.PrepareForTestWithJavaDefaultModules,
+ phony.PrepareForTestWithPhony,
PrepareForTestWithFilesystemBuildComponents,
)
@@ -40,11 +49,108 @@ func TestFileSystemDeps(t *testing.T) {
result := fixture.RunTestWithBp(t, `
android_filesystem {
name: "myfilesystem",
+ multilib: {
+ common: {
+ deps: [
+ "bpf.o",
+ "phony",
+ ],
+ },
+ lib32: {
+ deps: [
+ "foo",
+ "libbar",
+ ],
+ },
+ lib64: {
+ deps: [
+ "libbar",
+ ],
+ },
+ },
+ compile_multilib: "both",
+ }
+
+ bpf {
+ name: "bpf.o",
+ srcs: ["bpf.c"],
+ }
+
+ cc_binary {
+ name: "foo",
+ compile_multilib: "prefer32",
+ }
+
+ cc_library {
+ name: "libbar",
+ required: ["libbaz"],
+ target: {
+ platform: {
+ required: ["lib_platform_only"],
+ },
+ },
+ }
+
+ cc_library {
+ name: "libbaz",
+ }
+
+ cc_library {
+ name: "lib_platform_only",
+ }
+
+ phony {
+ name: "phony",
+ required: [
+ "libquz",
+ "myapp",
+ ],
+ }
+
+ cc_library {
+ name: "libquz",
+ }
+
+ android_app {
+ name: "myapp",
+ platform_apis: true,
+ installable: true,
}
`)
// produces "myfilesystem.img"
result.ModuleForTests("myfilesystem", "android_common").Output("myfilesystem.img")
+
+ fs := result.ModuleForTests("myfilesystem", "android_common").Module().(*filesystem)
+ expected := []string{
+ "app/myapp/myapp.apk",
+ "bin/foo",
+ "lib/libbar.so",
+ "lib64/libbar.so",
+ "lib64/libbaz.so",
+ "lib64/libquz.so",
+ "lib64/lib_platform_only.so",
+ "etc/bpf/bpf.o",
+ }
+ for _, e := range expected {
+ android.AssertStringListContains(t, "missing entry", fs.entries, e)
+ }
+}
+
+func TestIncludeMakeBuiltFiles(t *testing.T) {
+ result := fixture.RunTestWithBp(t, `
+ android_filesystem {
+ name: "myfilesystem",
+ include_make_built_files: "system",
+ }
+ `)
+
+ output := result.ModuleForTests("myfilesystem", "android_common").Output("myfilesystem.img")
+
+ stampFile := filepath.Join(result.Config.OutDir(), "target/product/test_device/obj/PACKAGING/system_intermediates/staging_dir.stamp")
+ fileListFile := filepath.Join(result.Config.OutDir(), "target/product/test_device/obj/PACKAGING/system_intermediates/file_list.txt")
+ android.AssertStringListContains(t, "deps of filesystem must include the staging dir stamp file", output.Implicits.Strings(), stampFile)
+ android.AssertStringListContains(t, "deps of filesystem must include the staging dir file list", output.Implicits.Strings(), fileListFile)
}
func TestFileSystemFillsLinkerConfigWithStubLibs(t *testing.T) {
@@ -193,43 +299,6 @@ func TestAvbAddHashFooter(t *testing.T) {
cmd, "--include_descriptors_from_image ")
}
-func TestFileSystemShouldInstallCoreVariantIfTargetBuildAppsIsSet(t *testing.T) {
- context := android.GroupFixturePreparers(
- fixture,
- android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
- variables.Unbundled_build_apps = []string{"bar"}
- }),
- )
- result := context.RunTestWithBp(t, `
- android_system_image {
- name: "myfilesystem",
- deps: [
- "libfoo",
- ],
- linker_config_src: "linker.config.json",
- }
-
- cc_library {
- name: "libfoo",
- shared_libs: [
- "libbar",
- ],
- stl: "none",
- }
-
- cc_library {
- name: "libbar",
- sdk_version: "9",
- stl: "none",
- }
- `)
-
- inputs := result.ModuleForTests("myfilesystem", "android_common").Output("deps.zip").Implicits
- android.AssertStringListContains(t, "filesystem should have libbar even for unbundled build",
- inputs.Strings(),
- "out/soong/.intermediates/libbar/android_arm64_armv8-a_shared/libbar.so")
-}
-
func TestFileSystemWithCoverageVariants(t *testing.T) {
context := android.GroupFixturePreparers(
fixture,
@@ -268,7 +337,7 @@ func TestFileSystemWithCoverageVariants(t *testing.T) {
`)
filesystem := result.ModuleForTests("myfilesystem", "android_common_cov")
- inputs := filesystem.Output("deps.zip").Implicits
+ inputs := filesystem.Output("myfilesystem.img").Implicits
android.AssertStringListContains(t, "filesystem should have libfoo(cov)",
inputs.Strings(),
"out/soong/.intermediates/libfoo/android_arm64_armv8-a_shared_cov/libfoo.so")
@@ -282,3 +351,212 @@ func TestFileSystemWithCoverageVariants(t *testing.T) {
t.Error("prebuilt should use cov variant of filesystem")
}
}
+
+func TestSystemImageDefaults(t *testing.T) {
+ result := fixture.RunTestWithBp(t, `
+ android_filesystem_defaults {
+ name: "defaults",
+ multilib: {
+ common: {
+ deps: [
+ "phony",
+ ],
+ },
+ lib64: {
+ deps: [
+ "libbar",
+ ],
+ },
+ },
+ compile_multilib: "both",
+ }
+
+ android_system_image {
+ name: "system",
+ defaults: ["defaults"],
+ multilib: {
+ lib32: {
+ deps: [
+ "foo",
+ "libbar",
+ ],
+ },
+ },
+ }
+
+ cc_binary {
+ name: "foo",
+ compile_multilib: "prefer32",
+ }
+
+ cc_library {
+ name: "libbar",
+ required: ["libbaz"],
+ }
+
+ cc_library {
+ name: "libbaz",
+ }
+
+ phony {
+ name: "phony",
+ required: ["libquz"],
+ }
+
+ cc_library {
+ name: "libquz",
+ }
+ `)
+
+ fs := result.ModuleForTests("system", "android_common").Module().(*systemImage)
+ expected := []string{
+ "bin/foo",
+ "lib/libbar.so",
+ "lib64/libbar.so",
+ "lib64/libbaz.so",
+ "lib64/libquz.so",
+ }
+ for _, e := range expected {
+ android.AssertStringListContains(t, "missing entry", fs.entries, e)
+ }
+}
+
+func TestInconsistentPartitionTypesInDefaults(t *testing.T) {
+ fixture.ExtendWithErrorHandler(android.FixtureExpectsOneErrorPattern(
+ "doesn't match with the partition type")).
+ RunTestWithBp(t, `
+ android_filesystem_defaults {
+ name: "system_ext_def",
+ partition_type: "system_ext",
+ }
+
+ android_filesystem_defaults {
+ name: "system_def",
+ partition_type: "system",
+ defaults: ["system_ext_def"],
+ }
+
+ android_system_image {
+ name: "system",
+ defaults: ["system_def"],
+ }
+ `)
+}
+
+func TestPreventDuplicatedEntries(t *testing.T) {
+ fixture.ExtendWithErrorHandler(android.FixtureExpectsOneErrorPattern(
+ "packaging conflict at")).
+ RunTestWithBp(t, `
+ android_filesystem {
+ name: "fs",
+ deps: [
+ "foo",
+ "foo_dup",
+ ],
+ }
+
+ cc_binary {
+ name: "foo",
+ }
+
+ cc_binary {
+ name: "foo_dup",
+ stem: "foo",
+ }
+ `)
+}
+
+func TestTrackPhonyAsRequiredDep(t *testing.T) {
+ result := fixture.RunTestWithBp(t, `
+ android_filesystem {
+ name: "fs",
+ deps: ["foo"],
+ }
+
+ cc_binary {
+ name: "foo",
+ required: ["phony"],
+ }
+
+ phony {
+ name: "phony",
+ required: ["libbar"],
+ }
+
+ cc_library {
+ name: "libbar",
+ }
+ `)
+
+ fs := result.ModuleForTests("fs", "android_common").Module().(*filesystem)
+ expected := []string{
+ "bin/foo",
+ "lib64/libbar.so",
+ }
+ for _, e := range expected {
+ android.AssertStringListContains(t, "missing entry", fs.entries, e)
+ }
+}
+
+func TestFilterOutUnsupportedArches(t *testing.T) {
+ result := fixture.RunTestWithBp(t, `
+ android_filesystem {
+ name: "fs_64_only",
+ deps: ["foo"],
+ }
+
+ android_filesystem {
+ name: "fs_64_32",
+ compile_multilib: "both",
+ deps: ["foo"],
+ }
+
+ cc_binary {
+ name: "foo",
+ required: ["phony"],
+ }
+
+ phony {
+ name: "phony",
+ required: [
+ "libbar",
+ "app",
+ ],
+ }
+
+ cc_library {
+ name: "libbar",
+ }
+
+ android_app {
+ name: "app",
+ srcs: ["a.java"],
+ platform_apis: true,
+ }
+ `)
+ testcases := []struct {
+ fsName string
+ expected []string
+ unexpected []string
+ }{
+ {
+ fsName: "fs_64_only",
+ expected: []string{"app/app/app.apk", "bin/foo", "lib64/libbar.so"},
+ unexpected: []string{"lib/libbar.so"},
+ },
+ {
+ fsName: "fs_64_32",
+ expected: []string{"app/app/app.apk", "bin/foo", "lib64/libbar.so", "lib/libbar.so"},
+ unexpected: []string{},
+ },
+ }
+ for _, c := range testcases {
+ fs := result.ModuleForTests(c.fsName, "android_common").Module().(*filesystem)
+ for _, e := range c.expected {
+ android.AssertStringListContains(t, "missing entry", fs.entries, e)
+ }
+ for _, e := range c.unexpected {
+ android.AssertStringListDoesNotContain(t, "unexpected entry", fs.entries, e)
+ }
+ }
+}
diff --git a/filesystem/fsverity_metadata.go b/filesystem/fsverity_metadata.go
new file mode 100644
index 000000000..d7bb654b9
--- /dev/null
+++ b/filesystem/fsverity_metadata.go
@@ -0,0 +1,179 @@
+// 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
+
+ // APK libraries to link against, for etc/security/fsverity/BuildManifest.apk
+ Libs []string `android:"path"`
+}
+
+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')
+ f.appendToEntry(ctx, destPath)
+ }
+
+ // 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(' ')
+ f.appendToEntry(ctx, manifestPbPath)
+
+ 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)
+ f.appendToEntry(ctx, manifestGeneratorListPath.OutputPath)
+
+ // STEP 2-2: generate BuildManifest.apk (unsigned)
+ aapt2Path := ctx.Config().HostToolPath(ctx, "aapt2")
+ apkPath := rebasedDir.Join(ctx, "etc", "security", "fsverity", "BuildManifest.apk")
+ idsigPath := rebasedDir.Join(ctx, "etc", "security", "fsverity", "BuildManifest.apk.idsig")
+ manifestTemplatePath := android.PathForSource(ctx, "system/security/fsverity/AndroidManifest.xml")
+ libs := android.PathsForModuleSrc(ctx, f.properties.Fsverity.Libs)
+ cmd.Implicit(aapt2Path)
+ cmd.Implicit(manifestTemplatePath)
+ cmd.Implicits(libs)
+ cmd.ImplicitOutput(apkPath)
+
+ sb.WriteString(aapt2Path.String())
+ sb.WriteString(" link -o ")
+ sb.WriteString(apkPath.String())
+ sb.WriteString(" -A ")
+ sb.WriteString(assetsPath.String())
+ for _, lib := range libs {
+ sb.WriteString(" -I ")
+ sb.WriteString(lib.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')
+
+ f.appendToEntry(ctx, apkPath)
+
+ // 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)
+ cmd.ImplicitOutput(idsigPath)
+ 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')
+
+ f.appendToEntry(ctx, idsigPath)
+
+ android.WriteExecutableFileRuleVerbatim(ctx, fsverityBuilderPath, sb.String())
+}
diff --git a/filesystem/logical_partition.go b/filesystem/logical_partition.go
index e2f7d7bdf..e483fe472 100644
--- a/filesystem/logical_partition.go
+++ b/filesystem/logical_partition.go
@@ -185,6 +185,8 @@ func (l *logicalPartition) GenerateAndroidBuildActions(ctx android.ModuleContext
l.installDir = android.PathForModuleInstall(ctx, "etc")
ctx.InstallFile(l.installDir, l.installFileName(), l.output)
+
+ ctx.SetOutputFiles([]android.Path{l.output}, "")
}
// Add a rule that converts the filesystem for the given partition to the given rule builder. The
@@ -231,13 +233,3 @@ func (l *logicalPartition) OutputPath() android.Path {
func (l *logicalPartition) SignedOutputPath() android.Path {
return nil // logical partition is not signed by itself
}
-
-var _ android.OutputFileProducer = (*logicalPartition)(nil)
-
-// Implements android.OutputFileProducer
-func (l *logicalPartition) OutputFiles(tag string) (android.Paths, error) {
- if tag == "" {
- return []android.Path{l.output}, nil
- }
- return nil, fmt.Errorf("unsupported module reference tag %q", tag)
-}
diff --git a/filesystem/raw_binary.go b/filesystem/raw_binary.go
index 1544ea788..ad36c2935 100644
--- a/filesystem/raw_binary.go
+++ b/filesystem/raw_binary.go
@@ -15,8 +15,6 @@
package filesystem
import (
- "fmt"
-
"github.com/google/blueprint"
"github.com/google/blueprint/proptools"
@@ -88,6 +86,8 @@ func (r *rawBinary) GenerateAndroidBuildActions(ctx android.ModuleContext) {
r.output = outputFile
r.installDir = android.PathForModuleInstall(ctx, "etc")
ctx.InstallFile(r.installDir, r.installFileName(), r.output)
+
+ ctx.SetOutputFiles([]android.Path{r.output}, "")
}
var _ android.AndroidMkEntriesProvider = (*rawBinary)(nil)
@@ -109,13 +109,3 @@ func (r *rawBinary) OutputPath() android.Path {
func (r *rawBinary) SignedOutputPath() android.Path {
return nil
}
-
-var _ android.OutputFileProducer = (*rawBinary)(nil)
-
-// Implements android.OutputFileProducer
-func (r *rawBinary) OutputFiles(tag string) (android.Paths, error) {
- if tag == "" {
- return []android.Path{r.output}, nil
- }
- return nil, fmt.Errorf("unsupported module reference tag %q", tag)
-}
diff --git a/filesystem/system_image.go b/filesystem/system_image.go
index 75abf702e..69d922df9 100644
--- a/filesystem/system_image.go
+++ b/filesystem/system_image.go
@@ -37,12 +37,15 @@ func systemImageFactory() android.Module {
module := &systemImage{}
module.AddProperties(&module.properties)
module.filesystem.buildExtraFiles = module.buildExtraFiles
- module.filesystem.filterPackagingSpecs = module.filterPackagingSpecs
+ module.filesystem.filterPackagingSpec = module.filterPackagingSpec
initFilesystemModule(&module.filesystem)
return module
}
func (s *systemImage) buildExtraFiles(ctx android.ModuleContext, root android.OutputPath) android.OutputPaths {
+ if s.filesystem.properties.Partition_type != nil {
+ ctx.PropertyErrorf("partition_type", "partition_type must be unset on an android_system_image module. It is assumed to be 'system'.")
+ }
lc := s.buildLinkerConfigFile(ctx, root)
// Add more files if needed
return []android.OutputPath{lc}
@@ -53,30 +56,48 @@ func (s *systemImage) buildLinkerConfigFile(ctx android.ModuleContext, root andr
output := root.Join(ctx, "system", "etc", "linker.config.pb")
// we need "Module"s for packaging items
- var otherModules []android.Module
+ modulesInPackageByModule := make(map[android.Module]bool)
+ modulesInPackageByName := make(map[string]bool)
+
deps := s.gatherFilteredPackagingSpecs(ctx)
ctx.WalkDeps(func(child, parent android.Module) bool {
for _, ps := range child.PackagingSpecs() {
if _, ok := deps[ps.RelPathInPackage()]; ok {
- otherModules = append(otherModules, child)
+ modulesInPackageByModule[child] = true
+ modulesInPackageByName[child.Name()] = true
+ return true
}
}
return true
})
+ provideModules := make([]android.Module, 0, len(modulesInPackageByModule))
+ for mod := range modulesInPackageByModule {
+ provideModules = append(provideModules, mod)
+ }
+
+ var requireModules []android.Module
+ ctx.WalkDeps(func(child, parent android.Module) bool {
+ _, parentInPackage := modulesInPackageByModule[parent]
+ _, childInPackageName := modulesInPackageByName[child.Name()]
+
+ // When parent is in the package, and child (or its variant) is not, this can be from an interface.
+ if parentInPackage && !childInPackageName {
+ requireModules = append(requireModules, child)
+ }
+ return true
+ })
+
builder := android.NewRuleBuilder(pctx, ctx)
- linkerconfig.BuildLinkerConfig(ctx, builder, input, otherModules, output)
+ linkerconfig.BuildLinkerConfig(ctx, builder, input, provideModules, requireModules, output)
builder.Build("conv_linker_config", "Generate linker config protobuf "+output.String())
return output
}
-// Filter the result of GatherPackagingSpecs to discard items targeting outside "system" partition.
-// Note that "apex" module installs its contents to "apex"(fake partition) as well
+// Filter the result of GatherPackagingSpecs to discard items targeting outside "system" / "root"
+// partition. Note that "apex" module installs its contents to "apex"(fake partition) as well
// for symbol lookup by imitating "activated" paths.
-func (s *systemImage) filterPackagingSpecs(specs map[string]android.PackagingSpec) {
- for k, ps := range specs {
- if ps.Partition() != "system" {
- delete(specs, k)
- }
- }
+func (s *systemImage) filterPackagingSpec(ps android.PackagingSpec) bool {
+ return s.filesystem.filterInstallablePackagingSpec(ps) &&
+ (ps.Partition() == "system" || ps.Partition() == "root")
}
diff --git a/filesystem/vbmeta.go b/filesystem/vbmeta.go
index 63e0abaac..0c6e7f428 100644
--- a/filesystem/vbmeta.go
+++ b/filesystem/vbmeta.go
@@ -63,6 +63,17 @@ type vbmetaProperties struct {
// List of chained partitions that this vbmeta deletages the verification.
Chained_partitions []chainedPartitionProperties
+
+ // List of key-value pair of avb properties
+ Avb_properties []avbProperty
+}
+
+type avbProperty struct {
+ // Key of given avb property
+ Key *string
+
+ // Value of given avb property
+ Value *string
}
type chainedPartitionProperties struct {
@@ -135,6 +146,20 @@ func (v *vbmeta) GenerateAndroidBuildActions(ctx android.ModuleContext) {
}
cmd.FlagWithArg("--rollback_index_location ", strconv.Itoa(ril))
+ for _, avb_prop := range v.properties.Avb_properties {
+ key := proptools.String(avb_prop.Key)
+ if key == "" {
+ ctx.PropertyErrorf("avb_properties", "key must be specified")
+ continue
+ }
+ value := proptools.String(avb_prop.Value)
+ if value == "" {
+ ctx.PropertyErrorf("avb_properties", "value must be specified")
+ continue
+ }
+ cmd.FlagWithArg("--prop ", key+":"+value)
+ }
+
for _, p := range ctx.GetDirectDepsWithTag(vbmetaPartitionDep) {
f, ok := p.(Filesystem)
if !ok {
@@ -186,6 +211,8 @@ func (v *vbmeta) GenerateAndroidBuildActions(ctx android.ModuleContext) {
v.installDir = android.PathForModuleInstall(ctx, "etc")
ctx.InstallFile(v.installDir, v.installFileName(), v.output)
+
+ ctx.SetOutputFiles([]android.Path{v.output}, "")
}
// Returns the embedded shell command that prints the rollback index
@@ -263,13 +290,3 @@ func (v *vbmeta) OutputPath() android.Path {
func (v *vbmeta) SignedOutputPath() android.Path {
return v.OutputPath() // vbmeta is always signed
}
-
-var _ android.OutputFileProducer = (*vbmeta)(nil)
-
-// Implements android.OutputFileProducer
-func (v *vbmeta) OutputFiles(tag string) (android.Paths, error) {
- if tag == "" {
- return []android.Path{v.output}, nil
- }
- return nil, fmt.Errorf("unsupported module reference tag %q", tag)
-}