summaryrefslogtreecommitdiff
path: root/filesystem/filesystem.go
diff options
context:
space:
mode:
Diffstat (limited to 'filesystem/filesystem.go')
-rw-r--r--filesystem/filesystem.go344
1 files changed, 321 insertions, 23 deletions
diff --git a/filesystem/filesystem.go b/filesystem/filesystem.go
index c6181bc15..cf9871701 100644
--- a/filesystem/filesystem.go
+++ b/filesystem/filesystem.go
@@ -16,24 +16,70 @@ package filesystem
import (
"fmt"
+ "path/filepath"
+ "strings"
"android/soong/android"
"github.com/google/blueprint"
+ "github.com/google/blueprint/proptools"
)
func init() {
- android.RegisterModuleType("android_filesystem", filesystemFactory)
+ registerBuildComponents(android.InitRegistrationContext)
+}
+
+func registerBuildComponents(ctx android.RegistrationContext) {
+ ctx.RegisterModuleType("android_filesystem", filesystemFactory)
}
type filesystem struct {
android.ModuleBase
android.PackagingBase
+ properties filesystemProperties
+
output android.OutputPath
installDir android.InstallPath
}
+type symlinkDefinition struct {
+ Target *string
+ Name *string
+}
+
+type filesystemProperties struct {
+ // When set to true, sign the image with avbtool. Default is false.
+ Use_avb *bool
+
+ // Path to the private key that avbtool will use to sign this filesystem image.
+ // TODO(jiyong): allow apex_key to be specified here
+ Avb_private_key *string `android:"path"`
+
+ // Hash and signing algorithm for avbtool. Default is SHA256_RSA4096.
+ Avb_algorithm *string
+
+ // Name of the partition stored in vbmeta desc. Defaults to the name of this module.
+ Partition_name *string
+
+ // Type of the filesystem. Currently, ext4, cpio, and compressed_cpio are supported. Default
+ // is ext4.
+ Type *string
+
+ // file_contexts file to make image. Currently, only ext4 is supported.
+ File_contexts *string `android:"path"`
+
+ // Base directory relative to root, to which deps are installed, e.g. "system". Default is "."
+ // (root).
+ Base_dir *string
+
+ // Directories to be created under root. e.g. /dev, /proc, etc.
+ Dirs []string
+
+ // Symbolic links to be created under root with "ln -sf <target> <name>".
+ Symlinks []symlinkDefinition
+}
+
// android_filesystem packages a set of modules and their transitive dependencies into a filesystem
// image. The filesystem images are expected to be mounted in the target device, which means the
// modules in the filesystem image are built for the target device (i.e. Android, not Linux host).
@@ -41,17 +87,45 @@ type filesystem struct {
// partitions like system.img. For example, cc_library modules are placed under ./lib[64] directory.
func filesystemFactory() android.Module {
module := &filesystem{}
+ module.AddProperties(&module.properties)
android.InitPackageModule(module)
android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
return module
}
-var dependencyTag = struct{ blueprint.BaseDependencyTag }{}
+var dependencyTag = struct {
+ blueprint.BaseDependencyTag
+ android.PackagingItemAlwaysDepTag
+}{}
func (f *filesystem) DepsMutator(ctx android.BottomUpMutatorContext) {
f.AddDeps(ctx, dependencyTag)
}
+type fsType int
+
+const (
+ ext4Type fsType = iota
+ compressedCpioType
+ cpioType // uncompressed
+ unknown
+)
+
+func (f *filesystem) fsType(ctx android.ModuleContext) fsType {
+ typeStr := proptools.StringDefault(f.properties.Type, "ext4")
+ switch typeStr {
+ case "ext4":
+ return ext4Type
+ case "compressed_cpio":
+ return compressedCpioType
+ case "cpio":
+ return cpioType
+ default:
+ ctx.PropertyErrorf("type", "%q not supported", typeStr)
+ return unknown
+ }
+}
+
func (f *filesystem) installFileName() string {
return f.BaseModuleName() + ".img"
}
@@ -59,39 +133,229 @@ func (f *filesystem) installFileName() string {
var pctx = android.NewPackageContext("android/soong/filesystem")
func (f *filesystem) GenerateAndroidBuildActions(ctx android.ModuleContext) {
- zipFile := android.PathForModuleOut(ctx, "temp.zip").OutputPath
- f.CopyDepsToZip(ctx, zipFile)
+ switch f.fsType(ctx) {
+ case ext4Type:
+ f.output = f.buildImageUsingBuildImage(ctx)
+ case compressedCpioType:
+ f.output = f.buildCpioImage(ctx, true)
+ case cpioType:
+ f.output = f.buildCpioImage(ctx, false)
+ default:
+ return
+ }
- rootDir := android.PathForModuleOut(ctx, "root").OutputPath
+ f.installDir = android.PathForModuleInstall(ctx, "etc")
+ ctx.InstallFile(f.installDir, f.installFileName(), f.output)
+}
+
+// root zip will contain stuffs like dirs or symlinks.
+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())
+
+ // create dirs and symlinks
+ for _, dir := range f.properties.Dirs {
+ // OutputPath.Join verifies dir
+ builder.Command().Text("mkdir -p").Text(rootDir.Join(ctx, dir).String())
+ }
+
+ for _, symlink := range f.properties.Symlinks {
+ name := strings.TrimSpace(proptools.String(symlink.Name))
+ target := strings.TrimSpace(proptools.String(symlink.Target))
+
+ if name == "" {
+ ctx.PropertyErrorf("symlinks", "Name can't be empty")
+ continue
+ }
+
+ if target == "" {
+ ctx.PropertyErrorf("symlinks", "Target can't be empty")
+ continue
+ }
+
+ // OutputPath.Join verifies name. don't need to verify target.
+ dst := rootDir.Join(ctx, name)
+
+ builder.Command().Text("mkdir -p").Text(filepath.Dir(dst.String()))
+ builder.Command().Text("ln -sf").Text(proptools.ShellEscape(target)).Text(dst.String())
+ }
+
+ zipOut := android.PathForModuleGen(ctx, "root.zip").OutputPath
+
+ builder.Command().
+ BuiltTool("soong_zip").
+ 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
+ builder.Command().Text("rm -rf").Text(rootDir.String())
+
+ builder.Build("zip_root", fmt.Sprintf("zipping root contents for %s", ctx.ModuleName()))
+ return zipOut
+}
+
+func (f *filesystem) buildImageUsingBuildImage(ctx android.ModuleContext) android.OutputPath {
+ depsZipFile := android.PathForModuleOut(ctx, "deps.zip").OutputPath
+ f.CopyDepsToZip(ctx, depsZipFile)
+
+ 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
+
+ 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(zipFile)
-
- mkuserimg := ctx.Config().HostToolPath(ctx, "mkuserimg_mke2fs")
- propFile := android.PathForModuleOut(ctx, "prop").OutputPath
- // TODO(jiyong): support more filesystem types other than ext4
- propsText := fmt.Sprintf(`mount_point=system\n`+
- `fs_type=ext4\n`+
- `use_dynamic_partition_size=true\n`+
- `ext_mkuserimg=%s\n`, mkuserimg.String())
- builder.Command().Text("echo").Flag("-e").Flag(`"` + propsText + `"`).
- Text(">").Output(propFile).
- Implicit(mkuserimg)
-
- f.output = android.PathForModuleOut(ctx, "filesystem.img").OutputPath
+ Input(rootZip).
+ Input(rebasedDepsZip)
+
+ propFile, toolDeps := f.buildPropFile(ctx)
+ output := android.PathForModuleOut(ctx, f.installFileName()).OutputPath
builder.Command().BuiltTool("build_image").
Text(rootDir.String()). // input directory
Input(propFile).
- Output(f.output).
+ Implicits(toolDeps).
+ Output(output).
Text(rootDir.String()) // directory where to find fs_config_files|dirs
// rootDir is not deleted. Might be useful for quick inspection.
builder.Build("build_filesystem_image", fmt.Sprintf("Creating filesystem %s", f.BaseModuleName()))
- f.installDir = android.PathForModuleInstall(ctx, "etc")
- ctx.InstallFile(f.installDir, f.installFileName(), f.output)
+ return output
+}
+
+func (f *filesystem) buildFileContexts(ctx android.ModuleContext) android.OutputPath {
+ builder := android.NewRuleBuilder(pctx, ctx)
+ fcBin := android.PathForModuleOut(ctx, "file_contexts.bin")
+ builder.Command().BuiltTool("sefcontext_compile").
+ FlagWithOutput("-o ", fcBin).
+ Input(android.PathForModuleSrc(ctx, proptools.String(f.properties.File_contexts)))
+ builder.Build("build_filesystem_file_contexts", fmt.Sprintf("Creating filesystem file contexts for %s", f.BaseModuleName()))
+ return fcBin.OutputPath
+}
+
+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
+ addStr := func(name string, value string) {
+ props = append(props, prop{name, value})
+ }
+ addPath := func(name string, path android.Path) {
+ props = append(props, prop{name, path.String()})
+ deps = append(deps, path)
+ }
+
+ // Type string that build_image.py accepts.
+ fsTypeStr := func(t fsType) string {
+ switch t {
+ // TODO(jiyong): add more types like f2fs, erofs, etc.
+ case ext4Type:
+ return "ext4"
+ }
+ panic(fmt.Errorf("unsupported fs type %v", t))
+ }
+
+ addStr("fs_type", fsTypeStr(f.fsType(ctx)))
+ addStr("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.
+ for _, t := range []string{"mke2fs", "e2fsdroid", "tune2fs"} {
+ deps = append(deps, ctx.Config().HostToolPath(ctx, t))
+ }
+
+ if proptools.Bool(f.properties.Use_avb) {
+ addStr("avb_hashtree_enable", "true")
+ addPath("avb_avbtool", ctx.Config().HostToolPath(ctx, "avbtool"))
+ algorithm := proptools.StringDefault(f.properties.Avb_algorithm, "SHA256_RSA4096")
+ addStr("avb_algorithm", algorithm)
+ key := android.PathForModuleSrc(ctx, proptools.String(f.properties.Avb_private_key))
+ addPath("avb_key_path", key)
+ addStr("avb_add_hashtree_footer_args", "--do_not_generate_fec")
+ partitionName := proptools.StringDefault(f.properties.Partition_name, f.Name())
+ addStr("partition_name", partitionName)
+ }
+
+ if proptools.String(f.properties.File_contexts) != "" {
+ addPath("selinux_fc", f.buildFileContexts(ctx))
+ }
+
+ 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()))
+ return propFile, deps
+}
+
+func (f *filesystem) buildCpioImage(ctx android.ModuleContext, compressed bool) android.OutputPath {
+ if proptools.Bool(f.properties.Use_avb) {
+ ctx.PropertyErrorf("use_avb", "signing compresed cpio image using avbtool is not supported."+
+ "Consider adding this to bootimg module and signing the entire boot image.")
+ }
+
+ if proptools.String(f.properties.File_contexts) != "" {
+ ctx.PropertyErrorf("file_contexts", "file_contexts is not supported for compressed cpio image.")
+ }
+
+ depsZipFile := android.PathForModuleOut(ctx, "deps.zip").OutputPath
+ f.CopyDepsToZip(ctx, depsZipFile)
+
+ 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
+
+ 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)
+
+ output := android.PathForModuleOut(ctx, f.installFileName()).OutputPath
+ cmd := builder.Command().
+ BuiltTool("mkbootfs").
+ Text(rootDir.String()) // input directory
+ if compressed {
+ cmd.Text("|").
+ BuiltTool("lz4").
+ Flag("--favor-decSpeed"). // for faster boot
+ Flag("-12"). // maximum compression level
+ Flag("-l"). // legacy format for kernel
+ Text(">").Output(output)
+ } else {
+ cmd.Text(">").Output(output)
+ }
+
+ // rootDir is not deleted. Might be useful for quick inspection.
+ builder.Build("build_cpio_image", fmt.Sprintf("Creating filesystem %s", f.BaseModuleName()))
+
+ return output
}
var _ android.AndroidMkEntriesProvider = (*filesystem)(nil)
@@ -102,10 +366,44 @@ func (f *filesystem) AndroidMkEntries() []android.AndroidMkEntries {
Class: "ETC",
OutputFile: android.OptionalPathForPath(f.output),
ExtraEntries: []android.AndroidMkExtraEntriesFunc{
- func(entries *android.AndroidMkEntries) {
+ func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
entries.SetString("LOCAL_MODULE_PATH", f.installDir.ToMakePath().String())
entries.SetString("LOCAL_INSTALLED_MODULE_STEM", f.installFileName())
},
},
}}
}
+
+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 {
+ android.Module
+ OutputPath() android.Path
+
+ // Returns the output file that is signed by avbtool. If this module is not signed, returns
+ // nil.
+ SignedOutputPath() android.Path
+}
+
+var _ Filesystem = (*filesystem)(nil)
+
+func (f *filesystem) OutputPath() android.Path {
+ return f.output
+}
+
+func (f *filesystem) SignedOutputPath() android.Path {
+ if proptools.Bool(f.properties.Use_avb) {
+ return f.OutputPath()
+ }
+ return nil
+}