Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  filesystem.go   Sprache: unbekannt

 
Spracherkennung für: .go vermutete Sprache: Unknown {[0] [0] [0]} [Methode: Schwerpunktbildung, einfache Gewichte, sechs Dimensionen]

// Copyright (C) 2020 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 (
 "crypto/sha256"
 "fmt"
 "io"
 "path/filepath"
 "slices"
 "sort"
 "strconv"
 "strings"

 "android/soong/android"
 "android/soong/cc"
 "android/soong/java"
 "android/soong/linkerconfig"

 "github.com/google/blueprint"
 "github.com/google/blueprint/depset"
 "github.com/google/blueprint/proptools"
)

//go:generate go run ../../blueprint/gobtools/codegen

var pctx = android.NewPackageContext("android/soong/filesystem")

func init() {
 registerBuildComponents(android.InitRegistrationContext)
 registerMutators(android.InitRegistrationContext)
 pctx.HostBinToolVariable("fileslist", "fileslist")
 pctx.HostBinToolVariable("fs_config", "fs_config")
 pctx.HostBinToolVariable("SoongZipCmd", "soong_zip")
 pctx.HostBinToolVariable("check_radio_versions", "check_radio_versions")
}

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)
 ctx.RegisterModuleType("bootimg", BootimgFactory)
 ctx.RegisterModuleType("raw_binary", rawBinaryFactory)
}

func registerMutators(ctx android.RegistrationContext) {
 ctx.PostApexMutators(func(ctx android.RegisterMutatorsContext) {
  ctx.BottomUp("add_autogenerated_rro_deps", addAutogeneratedRroDeps)
 })
}

var (
 // Remember to add referenced files to implicits!
 textFileProcessorRule = pctx.AndroidStaticRule("text_file_processing", blueprint.RuleParams{
  Command:         "build/soong/scripts/text_file_processor.py $in $out",
  CommandDeps:     []string{"build/soong/scripts/text_file_processor.py"},
  SandboxDisabled: true,
 })

 // Remember to add the output image file as an implicit dependency!
 installedFilesJsonRule = pctx.AndroidStaticRule("installed_files_json", blueprint.RuleParams{
  Command:         `${fileslist} ${rootDir} > ${out}`,
  CommandDeps:     []string{"${fileslist}"},
  SandboxDisabled: true,
 }, "rootDir")

 installedFilesTxtRule = pctx.AndroidStaticRule("installed_files_txt", blueprint.RuleParams{
  Command:         `build/make/tools/fileslist_util.py -c ${in} > ${out}`,
  CommandDeps:     []string{"build/make/tools/fileslist_util.py"},
  SandboxDisabled: true,
 })
 fsConfigRule = pctx.AndroidStaticRule("fs_config_rule", blueprint.RuleParams{
  Command:         `(cd ${rootDir}; find . -type d | sed 's,$$,/,'; find . \! -type d) | cut -c 3- | sort | sed 's,^,${prefix},' | ${fs_config} -C -D ${rootDir} -R "${prefix}" > ${out}`,
  CommandDeps:     []string{"${fs_config}"},
  SandboxDisabled: true,
 }, "rootDir", "prefix")
 zipFiles = pctx.AndroidStaticRule("SnapshotZipFiles", blueprint.RuleParams{
  Command:         `${SoongZipCmd}  -r $out.rsp -o $out`,
  CommandDeps:     []string{"${SoongZipCmd}"},
  Rspfile:         "$out.rsp",
  RspfileContent:  "$in",
  SandboxDisabled: true,
 })
)

type filesystem struct {
 android.ModuleBase
 android.PackagingBase
 android.DefaultableModuleBase
 blueprint.ModuleUsesIncrementalWalkDeps

 properties FilesystemProperties

 output     android.Path
 installDir android.InstallPath

 fileListFile android.Path

 // Keeps the entries installed from this filesystem
 entries []string

 // List of subpartitions installed in this filesystem
 subPartitions []string

 filesystemBuilder filesystemBuilder

 selinuxFc android.Path
 avbKey    android.Path

 overriddenModulesPackagingSpecs map[string]depset.DepSet[android.PackagingSpec]
}

type filesystemBuilder interface {
 BuildLinkerConfigFile(ctx android.ModuleContext, builder *android.RuleBuilder, rebasedDir android.OutputPath, fullInstallPaths *[]FullInstallPathInfo, platformGeneratedFiles *[]string)
 // Function that filters PackagingSpec in PackagingBase.GatherPackagingSpecs()
 FilterPackagingSpec(spec android.PackagingSpec) bool
 // Function that modifies PackagingSpec in PackagingBase.GatherPackagingSpecs() to customize.
 // For example, GSI system.img contains system_ext and product artifacts and their
 // relPathInPackage need to be rebased to system/system_ext and system/system_product.
 ModifyPackagingSpec(spec *android.PackagingSpec)

 // Function to check if the filesystem should not use `vintf_fragments` property,
 // but use `vintf_fragment` module type instead
 ShouldUseVintfFragmentModuleOnly() bool

 // Function to filter overridden modules' transtive packaging specs DepSet in
 // PackagingBase.GatherPackagingSpecs()
 FilterOverriddenModulesTransitivePackagingSpecs(depset.DepSet[android.PackagingSpec]) depset.DepSet[android.PackagingSpec]
}

var _ filesystemBuilder = (*filesystem)(nil)

type SymlinkDefinition struct {
 Target *string
 Name   *string
}

// CopyWithNamePrefix returns a new [SymlinkDefinition] with prefix added to Name.
func (s *SymlinkDefinition) CopyWithNamePrefix(prefix string) SymlinkDefinition {
 return SymlinkDefinition{
  Target: s.Target,
  Name:   proptools.StringPtr(filepath.Join(prefix, proptools.String(s.Name))),
 }
}

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"`

 // Signing algorithm for avbtool. Default is SHA256_RSA4096.
 Avb_algorithm *string

 // Hash algorithm used for avbtool (for descriptors). This is passed as hash_algorithm to
 // avbtool. Default is sha256.
 Avb_hash_algorithm *string

 // The security patch passed to as the com.android.build.<type>.security_patch avb property.
 Security_patch *string

 // Whether or not to use forward-error-correction codes when signing with AVB. Defaults to true.
 Use_fec *bool

 // The index used to prevent rollback of the image. Only used if use_avb is true.
 Rollback_index *int64

 // Rollback index location of this image. Must be 123, etc.
 Rollback_index_location *int64

 // Name of the partition stored in vbmeta desc. Defaults to the name of this module.
 Partition_name *string

 // Type of the filesystem. Currently, ext4, erofs, cpio, and compressed_cpio are supported. Default
 // is ext4.
 Type proptools.Configurable[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. These file contexts
 // will be compiled with sefcontext_compile
 File_contexts *string `android:"path"`

 // The selinux file contexts, after having already run them through sefcontext_compile
 Precompiled_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 proptools.Configurable[[]string]

 // List of filesystem modules to include in creating the partition. The root directory of
 // the provided filesystem modules are included in creating the partition.
 // This is only supported for cpio and compressed cpio filesystem types.
 Include_files_of []string

 // Symbolic links to be created under root with "ln -sf <target> <name>".
 Symlinks []SymlinkDefinition

 // Seconds since unix epoch to override timestamps of file entries
 Fake_timestamp *string

 // To produce hermetic filesystems, the timestamp of the file entries is pinned to a known
 // timestamp by default.
 // Setting `No_use_fixed_timestamp` to true will create unpinned timestamps.
 No_use_fixed_timestamp *bool

 // 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

 // 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

 // If this property is set to true, the filesystem will call ctx.UncheckedModule(), causing
 // it to not be built on checkbuilds. Used for the automatic migration from make to soong
 // build modules, where we want to emit some not-yet-working filesystems and we don't want them
 // to be built.
 Unchecked_module *bool `blueprint:"mutated"`

 Erofs ErofsProperties

 F2fs F2fsProperties

 Linker_config LinkerConfigProperties

 // Determines if the module is auto-generated from Soong or not. If the module is
 // auto-generated, its deps are exempted from visibility enforcement.
 Is_auto_generated *bool

 // Path to the dev nodes description file. This is only needed for building the ramdisk
 // partition and should not be explicitly specified.
 Dev_nodes_description_file *string `android:"path" blueprint:"mutated"`

 // Additional dependencies used for building android products
 Android_filesystem_deps AndroidFilesystemDeps

 // Name of the output. Default is $(module_name).img
 Stem *string

 // The size of the partition on the device. It will be a build error if this built partition
 // image exceeds this size.
 Partition_size *int64

 // Whether to format f2fs and ext4 in a way that supports casefolding
 Support_casefolding *bool

 // Whether to format f2fs and ext4 in a way that supports project quotas
 Support_project_quota *bool

 // Whether to enable per-file compression in f2fs
 Enable_compression *bool

 // F2FS block size which must be the same with the page size
 F2fs_blocksize *int64

 // Whether to format f2fs in a way that supports packed ssa feature
 Support_f2fs_packedssa *bool

 // Whether this partition is not supported by flashall.
 // If true, this partition will not be included in the `updatedpackage` dist artifact.
 No_flashall *bool

 // Run checkvintf on the vintf manifests of the filesystem
 Check_vintf *bool

 // Used by build_image
 Share_dup_blocks *bool

 // Name to use as `--ramdisk_name` when included as a fragment in a bootimg.
 Ramdisk_fragment_name *string

 // Name of android_prebuilt_system_module. This is only for experiment as of now and must not be
 // used for any release.
 Prebuilt_module_name *string

 // Whether to run the host_init_verifier check for this module. Defaults to true.
 Enable_host_init_verifier_check *bool
}

type AndroidFilesystemDeps struct {
 System     *string
 System_ext *string
 Product    *string
}

// Additional properties required to generate erofs FS partitions.
type ErofsProperties struct {
 // Compressor and Compression level passed to mkfs.erofs. e.g. (lz4hc,9)
 // Please see external/erofs-utils/README for complete documentation.
 Compressor proptools.Configurable[string]

 // Used as --compress-hints for mkfs.erofs
 Compress_hints proptools.Configurable[string] `android:"path"`

 Sparse *bool

 Pcluster_size *int64

 Block_size *int64

 Enable_dedupe *bool
}

// Additional properties required to generate f2fs FS partitions.
type F2fsProperties struct {
 Sparse *bool
}

type LinkerConfigProperties struct {

 // Build a linker.config.pb file
 Gen_linker_config *bool

 // List of files (in .json format) that will be converted to a linker config file (in .pb format).
 // The linker config file be installed in the filesystem at /etc/linker.config.pb
 Linker_config_srcs []string `android:"path"`
}

// 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).
// The modules are placed in the filesystem image just like they are installed to the ordinary
// partitions like system.img. For example, cc_library modules are placed under ./lib[64] directory.
func FilesystemFactory() android.Module {
 module := &filesystem{}
 module.filesystemBuilder = module
 initFilesystemModule(module, module)
 return module
}

// This does not include packaging specs. It is used for initializing prebuilt images and normal
// filesystem modules.
func initBaseFilesystemModule(module android.DefaultableModule, filesystemModule *filesystem) {
 module.AddProperties(&filesystemModule.properties)
 android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
 android.InitDefaultableModule(module)
}

func initFilesystemModule(module android.DefaultableModule, filesystemModule *filesystem) {
 android.InitPackageModule(filesystemModule)
 filesystemModule.PackagingBase.DepsCollectFirstTargetOnly = true
 filesystemModule.PackagingBase.AllowHighPriorityDeps = true
 initBaseFilesystemModule(module, filesystemModule)

 android.AddLoadHook(module, func(ctx android.LoadHookContext) {
  filesystemModule.setDevNodesDescriptionProp()
 })
}

type WalkAutoGeneratedRroDepsTag interface {
 // Marker method to differentiate depTag and structs that embed depTag from other tags
 WalkAutoGeneratedRroDeps()
}

type depTag struct {
 blueprint.BaseDependencyTag
 android.PackagingItemAlwaysDepTag
 WalkAutoGeneratedRroDepsTag
}

func (d depTag) WalkAutoGeneratedRroDeps() {}

var dependencyTag = depTag{}

type depTagWithVisibilityEnforcementBypass struct {
 depTag
}

type interPartitionDepTag struct {
 blueprint.BaseDependencyTag
}

var interPartitionDependencyTag = interPartitionDepTag{}

var interPartitionInstallDependencyTag = interPartitionDepTag{}

func (t interPartitionDepTag) IncludeVintfs() bool {
 return false
}

var _ android.InterPartitionIncludeVintfsInterface = (*interPartitionDepTag)(nil)

var _ android.ExcludeFromVisibilityEnforcementTag = (*depTagWithVisibilityEnforcementBypass)(nil)

func (t depTagWithVisibilityEnforcementBypass) ExcludeFromVisibilityEnforcement() {}

var dependencyTagWithVisibilityEnforcementBypass = depTagWithVisibilityEnforcementBypass{}

// ramdiskDevNodesDescription is the name of the filegroup module that provides the file that
// contains the description of dev nodes added to the CPIO archive for the ramdisk partition.
const ramdiskDevNodesDescription = "ramdisk_node_list"

func (f *filesystem) UseGenericConfig() bool {
 if proptools.Bool(f.properties.Is_auto_generated) {
  return false
 }
 return f.PartitionType() == "system"
}

func (f *filesystem) setDevNodesDescriptionProp() {
 if proptools.String(f.properties.Partition_name) == "ramdisk" {
  f.properties.Dev_nodes_description_file = proptools.StringPtr(":" + ramdiskDevNodesDescription)
 }
}

func (f *filesystem) RunHostInitVerifierCheck() bool {
 return proptools.BoolDefault(f.properties.Enable_host_init_verifier_check, true)
}

func (f *filesystem) addDepsDepTag() blueprint.DependencyTag {
 if proptools.Bool(f.properties.Is_auto_generated) {
  return dependencyTagWithVisibilityEnforcementBypass
 }
 return dependencyTag
}

func (f *filesystem) DepsMutator(ctx android.BottomUpMutatorContext) {
 f.AddDeps(ctx, f.addDepsDepTag())
 if f.properties.Android_filesystem_deps.System != nil {
  ctx.AddDependency(ctx.Module(), interPartitionDependencyTag, proptools.String(f.properties.Android_filesystem_deps.System))
 }
 if f.properties.Android_filesystem_deps.System_ext != nil {
  ctx.AddDependency(ctx.Module(), interPartitionDependencyTag, proptools.String(f.properties.Android_filesystem_deps.System_ext))
 }
 if f.properties.Android_filesystem_deps.Product != nil {
  if f.PartitionType() != "vendor" {
   ctx.ModuleErrorf("Product cannot be set for non-vendor partitions.")
  }
  ctx.AddDependency(ctx.Module(), interPartitionDependencyTag, proptools.String(f.properties.Android_filesystem_deps.Product))
 }
 for _, partition := range f.properties.Include_files_of {
  ctx.AddDependency(ctx.Module(), interPartitionInstallDependencyTag, partition)
 }
 // TODO: remove this once android_filesystem_prebuilt is fully implemented.
 if f.properties.Prebuilt_module_name != nil {
  ctx.AddDependency(ctx.Module(), android.PrebuiltDepTag, proptools.String(f.properties.Prebuilt_module_name))
 }

 ctx.AddHostToolDependencies("conv_linker_config")
}

type fsType int

const (
 ext4Type fsType = iota
 erofsType
 f2fsType
 compressedCpioType
 cpioType // uncompressed
 unknown
)

func (fs fsType) IsUnknown() bool {
 return fs == unknown
}

// Type string that build_image.py accepts.
func (t fsType) String() string {
 switch t {
 // TODO(372522486): add more types like f2fs, erofs, etc.
 case ext4Type:
  return "ext4"
 case erofsType:
  return "erofs"
 case f2fsType:
  return "f2fs"
 }
 panic(fmt.Errorf("unsupported fs type %d", t))
}

// @auto-generate: gob
type InstalledFilesStruct struct {
 Txt  android.Path
 Json android.Path
}

// @auto-generate: gob
type InstalledModuleInfo struct {
 Name      string
 Variation string
 Prebuilt  bool
}

// @auto-generate: gob
type FilesystemInfo struct {
 // The built filesystem image
 Output android.Path
 // Returns the output file that is signed by avbtool. If this module is not signed, returns
 // nil.
 SignedOutputPath android.Path
 // A text file containing the list of paths installed on the partition.
 FileListFile android.Path
 // The root staging directory used to build the output filesystem. If consuming this, make sure
 // to add a dependency on the Output file, as you cannot add dependencies on directories
 // in ninja.
 RootDir android.OutputPath
 // Extra root directories that are also built into the partition. Currently only used for
 // including the recovery partition files into the vendor_boot image.
 ExtraRootDirs android.Paths
 // The rebased staging directory used to build the output filesystem. If consuming this, make
 // sure to add a dependency on the Output file, as you cannot add dependencies on directories
 // in ninja. In many cases this is the same as RootDir, only in the system partition is it
 // different. There, it points to the "system" sub-directory of RootDir.
 RebasedDir android.OutputPath
 // Name of the module that produced this FilesystemInfo origionally. (though it may be
 // re-exported by super images or boot images)
 ModuleName string
 // The property file generated by this module and passed to build_image.
 // It's exported here so that system_other can reuse system's property file.
 BuildImagePropFile android.Path
 // Paths to all the tools referenced inside of the build image property file.
 BuildImagePropFileDeps android.Paths
 // Packaging specs to be installed on the system_other image, for the initial boot's dexpreopt.
 SpecsForSystemOther map[string]android.PackagingSpec

 FullInstallPaths []FullInstallPathInfo

 // Installed files dep set of this module and its dependency filesystem modules
 InstalledFilesDepSet depset.DepSet[InstalledFilesStruct]

 // Path to compress hints file for erofs filesystems
 // This will be nil for other fileystems like ext4
 ErofsCompressHints android.Path

 SelinuxFc android.Path

 FilesystemConfig android.Path

 Owners depset.DepSet[InstalledModuleInfo]

 HasFsverity bool

 PropFileForMiscInfo android.Path

 // Additional avb and partition size information.
 // `system_other` will use this information of `system` dep for misc_info.txt processing.
 PartitionSize    *int64
 UseAvb           bool
 AvbAlgorithm     string
 AvbHashAlgorithm string
 AvbKey           android.Path
 PartitionName    string
 NoFlashall       bool
 // HasOrIsRecovery returns true for recovery and for ramdisks with a recovery partition.
 HasOrIsRecovery bool

 // Results of check_vintf
 checkVintfLog android.Path

 Prebuilt bool
}

// FullInstallPathInfo contains information about the "full install" paths of all the files
// inside this partition. The full install paths are the files installed in
// out/target/product/<device>/<partition>. This is essentially legacy behavior, maintained for
// tools like adb sync and adevice, but we should update them to query the build system for the
// installed files no matter where they are.
// @auto-generate: gob
type FullInstallPathInfo struct {
 // RequiresFullInstall tells us if the origional module did the install to FullInstallPath
 // already. If it's false, the android_device module needs to emit the install rule.
 RequiresFullInstall bool
 // The "full install" paths for the files in this filesystem. This is the paths in the
 // out/target/product/<device>/<partition> folder. They're not used by this filesystem,
 // but can be depended on by the top-level android_device module to cause the staging
 // directories to be built.
 FullInstallPath android.InstallPath

 // The file that's copied to FullInstallPath. May be nil if SymlinkTarget is set or IsDir is
 // true.
 SourcePath android.Path

 // The target of the symlink, if this file is a symlink.
 SymlinkTarget string

 // If this file is a directory. Only used for empty directories, which are mostly mount points.
 IsDir bool
}

var FilesystemProvider = blueprint.NewProvider[FilesystemInfo]()

// @auto-generate: gob
type FilesystemDefaultsInfo struct{}

var FilesystemDefaultsInfoProvider = blueprint.NewProvider[FilesystemDefaultsInfo]()

func GetFsTypeFromString(ctx android.EarlyModuleContext, typeStr string) fsType {
 switch typeStr {
 case "ext4":
  return ext4Type
 case "erofs":
  return erofsType
 case "f2fs":
  return f2fsType
 case "compressed_cpio":
  return compressedCpioType
 case "cpio":
  return cpioType
 default:
  return unknown
 }
}

func (f *filesystem) fsType(ctx android.ModuleContext) fsType {
 typeStr := f.properties.Type.GetOrDefault(ctx, "ext4")
 fsType := GetFsTypeFromString(ctx, typeStr)
 if fsType == unknown {
  ctx.PropertyErrorf("type", "%q not supported", typeStr)
 }
 return fsType
}

func (f *filesystem) installFileName() string {
 return proptools.StringDefault(f.properties.Stem, f.BaseModuleName()+".img")
}

func (f *filesystem) partitionName() string {
 return proptools.StringDefault(f.properties.Partition_name, f.Name())
}

func (f *filesystem) FilterPackagingSpec(ps android.PackagingSpec) bool {
 // Filesystem module respects the installation semantic. A PackagingSpec from a module with
 // IsSkipInstall() is skipped.
 if ps.SkipInstall() {
  return false
 }
 // "apex" is a fake partition used to install files in out/target/product/<device>/apex/.
 // Don't include these files in the partition. We should also look into removing the following
 // TODO to check the PackagingSpec's partition against this filesystem's partition for all
 // modules, not just autogenerated ones, which will fix this as well.
 if ps.Partition() == "apex" {
  return false
 }
 if proptools.Bool(f.properties.Is_auto_generated) { // TODO (spandandas): Remove this.
  pt := f.PartitionType()

  // Due to the partition name of filesystem for data is 'userdata' but for PackagingSpec it is 'data'.
  if pt == "userdata" && ps.Partition() == "data" {
   return true
  }

  return ps.Partition() == pt || strings.HasPrefix(ps.Partition(), pt+"/")
 }
 return true
}

func (f *filesystem) ModifyPackagingSpec(ps *android.PackagingSpec) {
 // Sometimes, android.modulePartition() returns a path with >1 path components.
 // This makes the partition field of packagingSpecs have multiple components, like
 // "system/product". Right now, the filesystem module doesn't look at the partition field
 // when deciding what path to install the file under, only the RelPathInPackage field, so
 // we move the later path components from partition to relPathInPackage. This should probably
 // be revisited in the future.
 prefix := f.PartitionType() + "/"
 if strings.HasPrefix(ps.Partition(), prefix) {
  subPartition := strings.TrimPrefix(ps.Partition(), prefix)
  ps.SetPartition(f.PartitionType())
  ps.SetRelPathInPackage(filepath.Join(subPartition, ps.RelPathInPackage()))
  if !android.InList(subPartition, f.subPartitions) {
   f.subPartitions = append(f.subPartitions, subPartition)
  }
 }
}

func (f *filesystem) FilterOverriddenModulesTransitivePackagingSpecs(transitivePackagingSpecs depset.DepSet[android.PackagingSpec]) depset.DepSet[android.PackagingSpec] {

 // We cannot naively set minus all depset of overridden modules, as that might lead to
 // removing transitive deps that are referenced by other modules. Instead, find whether
 // the owner of the transitive packaging specs match any of the modules listed in
 // `overridden_deps` and then perform set minus on the discovered overridden transitive deps.
 var overriddenModulesForDepSet []string
 for _, ps := range transitivePackagingSpecs.ToList() {
  if _, ok := f.overriddenModulesPackagingSpecs[ps.Owner()]; ok {
   overriddenModulesForDepSet = append(overriddenModulesForDepSet, ps.Owner())
  }
 }

 for _, overriddenModuleName := range overriddenModulesForDepSet {
  transitivePackagingSpecs = transitivePackagingSpecs.SetMinus(f.overriddenModulesPackagingSpecs[overriddenModuleName])
 }
 return transitivePackagingSpecs
}

func buildInstalledFiles(ctx android.ModuleContext, partition string, rootDir android.Path, image android.Path) InstalledFilesStruct {
 fileName := "installed-files"
 if len(partition) > 0 {
  fileName += fmt.Sprintf("-%s", partition)
 }
 txt := android.PathForModuleOut(ctx, fmt.Sprintf("%s.txt", fileName))
 json := android.PathForModuleOut(ctx, fmt.Sprintf("%s.json", fileName))

 ctx.Build(pctx, android.BuildParams{
  Rule:        installedFilesJsonRule,
  Implicit:    image,
  Output:      json,
  Description: "Installed file list json",
  Args: map[string]string{
   "rootDir": rootDir.String(),
  },
 })

 ctx.Build(pctx, android.BuildParams{
  Rule:        installedFilesTxtRule,
  Input:       json,
  Output:      txt,
  Description: "Installed file list txt",
 })

 return InstalledFilesStruct{
  Txt:  txt,
  Json: json,
 }
}

func (f *filesystem) updateAvbInFsInfo(ctx android.ModuleContext, fsInfo *FilesystemInfo) {
 if proptools.Bool(f.properties.Use_avb) {
  fsInfo.UseAvb = true
  fsInfo.AvbAlgorithm = proptools.StringDefault(f.properties.Avb_algorithm, "SHA256_RSA4096")
  fsInfo.AvbHashAlgorithm = proptools.StringDefault(f.properties.Avb_hash_algorithm, "sha256")
  if f.properties.Avb_private_key != nil {
   fsInfo.AvbKey = android.PathForModuleSrc(ctx, *f.properties.Avb_private_key)
   f.avbKey = fsInfo.AvbKey
  }
 }
}

func (f *filesystem) gatherOverriddenModulesPackagingSpecs(ctx android.ModuleContext) {
 f.overriddenModulesPackagingSpecs = make(map[string]depset.DepSet[android.PackagingSpec])
 ctx.VisitDirectDepsProxyWithTag(android.OverriddenModuleDepTag, func(proxy android.ModuleProxy) {
  f.overriddenModulesPackagingSpecs[proxy.Name()] = android.GetInstallFiles(ctx, proxy).TransitivePackagingSpecs
 })
}

func (f *filesystem) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 validatePartitionType(ctx, f)
 if f.filesystemBuilder.ShouldUseVintfFragmentModuleOnly() {
  f.validateVintfFragments(ctx)
 }

 if len(f.properties.Include_files_of) > 0 && !android.InList(f.fsType(ctx), []fsType{compressedCpioType, cpioType}) {
  ctx.PropertyErrorf("include_files_of", "include_files_of is only supported for cpio and compressed cpio filesystem types.")
 }

 rootDir := android.PathForModuleOut(ctx, f.rootDirString()).OutputPath
 rebasedDir := rootDir
 if f.properties.Base_dir != nil {
  rebasedDir = rootDir.Join(ctx, *f.properties.Base_dir)
 }
 builder := android.NewRuleBuilder(pctx, ctx).SandboxDisabled()

 // Wipe the root dir to get rid of leftover files from prior builds
 builder.Command().Textf("rm -rf %s && mkdir -p %s", rootDir, rootDir)
 f.gatherOverriddenModulesPackagingSpecs(ctx)
 specs := f.gatherFilteredPackagingSpecs(ctx)

 var fullInstallPaths []FullInstallPathInfo
 for _, specRel := range android.SortedKeys(specs) {
  spec := specs[specRel]
  fullInstallPaths = append(fullInstallPaths, FullInstallPathInfo{
   FullInstallPath:     spec.FullInstallPath(),
   RequiresFullInstall: spec.RequiresFullInstall(),
   SourcePath:          spec.SrcPath(),
   SymlinkTarget:       spec.SymlinkTarget(),
  })
 }

 platformGeneratedFiles := []string{}
 f.entries = f.copyPackagingSpecs(ctx, builder, specs, rootDir, rebasedDir)
 f.verifyGenericConfig(ctx)
 f.buildNonDepsFiles(ctx, builder, rootDir, rebasedDir, &fullInstallPaths, &platformGeneratedFiles)
 f.buildFsverityMetadataFiles(ctx, builder, specs, rootDir, rebasedDir, &fullInstallPaths, &platformGeneratedFiles)
 f.buildEventLogtagsFile(ctx, builder, rebasedDir, &fullInstallPaths, &platformGeneratedFiles)
 f.buildAconfigFlagsFiles(ctx, builder, specs, rebasedDir, &fullInstallPaths, &platformGeneratedFiles)
 f.filesystemBuilder.BuildLinkerConfigFile(ctx, builder, rebasedDir, &fullInstallPaths, &platformGeneratedFiles)
 f.assembleVintfs(ctx, builder, rebasedDir, &fullInstallPaths, &platformGeneratedFiles)
 checkVintfLog := f.checkVintf(ctx, rebasedDir)
 // Assemeble the staging dir and output a timestamp
 builder.Command().Text("touch").Output(f.fileystemStagingDirTimestamp(ctx))
 builder.Build("assemble_filesystem_staging_dir", fmt.Sprintf("Assemble filesystem staging dir %s", f.BaseModuleName()))

 // Create a new rule builder for build_image
 f.installDir = android.PathForModuleInstall(ctx, "etc")

 // TODO: remove this once android_system_image_prebuilt is fully implemented.
 usePrebuilt := false
 var prebuiltInfo FilesystemInfo
 ctx.VisitDirectDepsProxyWithTag(android.PrebuiltDepTag, func(m android.ModuleProxy) {
  if fsProvider, ok := android.OtherModuleProvider(ctx, m, FilesystemProvider); ok {
   usePrebuilt = true
   prebuiltInfo = fsProvider
  } else {
   ctx.PropertyErrorf("prebuilt_module_name", "must provide filesystem")
  }

  if apexKeysInfo, ok := android.OtherModuleProvider(ctx, m, ApexKeyPathInfoProvider); ok {
   android.SetProvider(ctx, ApexKeyPathInfoProvider, apexKeysInfo)
  }
 })

 builder = android.NewRuleBuilder(pctx, ctx).SandboxDisabled()
 var buildImagePropFile android.Path
 var buildImagePropFileDeps android.Paths
 var extraRootDirs android.Paths
 var propFileForMiscInfo android.Path

 switch f.fsType(ctx) {
 case ext4Type, erofsType, f2fsType:
  buildImagePropFile, buildImagePropFileDeps = f.buildPropFile(ctx)
  propFileForMiscInfo = f.buildPropFileForMiscInfo(ctx)
  // TODO: remove this once android_system_image_prebuilt correctly implements prop files.
  if !usePrebuilt {
   output := android.PathForModuleOut(ctx, f.installFileName())
   f.buildImageUsingBuildImage(ctx, builder, buildImageParams{rootDir, buildImagePropFile, buildImagePropFileDeps, output})
   f.output = output
  } else {
   f.output = prebuiltInfo.Output
  }
 case compressedCpioType:
  f.output, extraRootDirs = f.buildCpioImage(ctx, builder, rootDir, true)
 case cpioType:
  f.output, extraRootDirs = f.buildCpioImage(ctx, builder, rootDir, false)
 default:
  return
 }

 ctx.InstallFile(f.installDir, f.installFileName(), f.output)
 ctx.SetOutputFiles([]android.Path{f.output}, "")

 if f.partitionName() == "recovery" {
  rootDir = rootDir.Join(ctx, "root")
 }

 fileListFile := android.PathForModuleOut(ctx, "fileList")
 android.WriteFileRule(ctx, fileListFile, f.installedFilesList())

 var partitionNameForInstalledFiles string
 switch f.partitionName() {
 case "system":
  partitionNameForInstalledFiles = ""
 case "vendor_ramdisk":
  partitionNameForInstalledFiles = "vendor-ramdisk"
 case "vendor_kernel_ramdisk":
  partitionNameForInstalledFiles = "vendor-kernel-ramdisk"
 default:
  partitionNameForInstalledFiles = f.partitionName()
 }

 var erofsCompressHints android.Path
 if v := f.properties.Erofs.Compress_hints.Get(ctx); v.IsPresent() {
  erofsCompressHints = android.PathForModuleSrc(ctx, v.Get())
 }

 var installedFilesStructList []InstalledFilesStruct
 if !usePrebuilt {
  installedFilesStructList = []InstalledFilesStruct{buildInstalledFiles(ctx, partitionNameForInstalledFiles, rebasedDir, f.output)}
  if f.partitionName() == "system" {
   rootDirForInstalledFiles := android.PathForModuleOut(ctx, "root_for_installed_files", "root")
   copyToRootTimestamp := android.PathForModuleOut(ctx, "root_copy_timestamp")

   builder := android.NewRuleBuilder(pctx, ctx).SandboxDisabled()
   builder.Command().Text("touch").Text(copyToRootTimestamp.String())
   builder.Command().Text("rm -rf").Text(rootDirForInstalledFiles.String())
   builder.Command().Text("mkdir -p").Text(rootDirForInstalledFiles.String())
   builder.Command().
    Text("rsync").
    Flag("-a").
    Flag("--checksum").
    Flag("--exclude='system/'").
    Text(rootDir.String() + "/").
    Text(rootDirForInstalledFiles.String()).
    Implicit(f.output).
    ImplicitOutput(copyToRootTimestamp)
   builder.Build("system_root_dir", "Construct system partition root dir")

   installedFilesStructList = append(installedFilesStructList, buildInstalledFiles(ctx, "root", rootDirForInstalledFiles, copyToRootTimestamp))
  }
 } else {
  // TODO: remove this once android_system_image_prebuilt correctly implements FilesystemInfo.
  rootDir = prebuiltInfo.RootDir
 }

 fsInfo := FilesystemInfo{
  Output:                 f.OutputPath(),
  SignedOutputPath:       f.SignedOutputPath(),
  FileListFile:           fileListFile,
  RootDir:                rootDir,
  ExtraRootDirs:          extraRootDirs,
  RebasedDir:             rebasedDir,
  ModuleName:             ctx.ModuleName(),
  BuildImagePropFile:     buildImagePropFile,
  BuildImagePropFileDeps: buildImagePropFileDeps,
  SpecsForSystemOther:    f.systemOtherFiles(ctx),
  FullInstallPaths:       fullInstallPaths,
  InstalledFilesDepSet: depset.New(
   depset.POSTORDER,
   installedFilesStructList,
   includeFilesInstalledFiles(ctx),
  ),
  ErofsCompressHints: erofsCompressHints,
  SelinuxFc:          f.selinuxFc,
  FilesystemConfig:   f.generateFilesystemConfig(ctx, rootDir, rebasedDir),
  Owners: depset.New(
   depset.POSTORDER,
   f.gatherOwners(specs),
   f.gatherSubPartitionOwners(ctx),
  ),
  HasFsverity:         f.properties.Fsverity.Inputs.GetOrDefault(ctx, nil) != nil,
  PropFileForMiscInfo: propFileForMiscInfo,
  PartitionSize:       f.properties.Partition_size,
  PartitionName:       f.partitionName(),
  HasOrIsRecovery:     f.hasOrIsRecovery(ctx),
  NoFlashall:          proptools.Bool(f.properties.No_flashall),
  checkVintfLog:       checkVintfLog,
  Prebuilt:            usePrebuilt,
 }
 f.updateAvbInFsInfo(ctx, &fsInfo)

 android.SetProvider(ctx, FilesystemProvider, fsInfo)

 android.SetProvider(ctx, android.PartitionTypeInfoProvider, android.PartitionTypeInfo{
  PartitionType: f.PartitionType(),
 })

 if f.properties.Ramdisk_fragment_name != nil {
  android.SetProvider(ctx, ramdiskFragmentInfoProvider, ramdiskFragmentInfo{
   Output:       f.OutputPath(),
   Ramdisk_name: proptools.String(f.properties.Ramdisk_fragment_name),
   RootDir:      rootDir,
  })
 }

 f.fileListFile = fileListFile

 f.setVbmetaPartitionProvider(ctx)

 // Dump metadata that can not be done in android/compliance-metadata.go
 complianceMetadataInfo := ctx.ComplianceMetadataInfo()
 filesContained := make([]string, 0, len(fullInstallPaths))
 for _, file := range fullInstallPaths {
  filesContained = append(filesContained, file.FullInstallPath.String())
 }
 complianceMetadataInfo.SetFilesContained(filesContained)
 complianceMetadataInfo.SetPlatformGeneratedFiles(platformGeneratedFiles)

 kernelModuleSrcDestPairs := []string{}
 ctx.VisitDirectDepsProxy(func(dep android.ModuleProxy) {
  if pkmi, ok := android.OtherModuleProvider(ctx, dep, android.PrebuiltKernelModulesComplianceMetadataProvider); ok {
   for i, _ := range pkmi.Srcs {
    kernelModuleSrcDestPairs = append(kernelModuleSrcDestPairs, pkmi.Srcs[i]+"::"+pkmi.Dests[i])
   }
  }
 })
 complianceMetadataInfo.SetKernelModuleCopyFiles(kernelModuleSrcDestPairs)
}

func (f *filesystem) fileystemStagingDirTimestamp(ctx android.ModuleContext) android.WritablePath {
 return android.PathForModuleOut(ctx, "staging_dir.timestamp")
}

func (f *filesystem) generateFilesystemConfig(ctx android.ModuleContext, rootDir android.Path, rebasedDir android.Path) android.Path {
 rootDirString := rootDir.String()
 prefix := f.partitionName() + "/"
 if f.partitionName() == "system" {
  rootDirString = rebasedDir.String()
 }
 if f.partitionName() == "ramdisk" || f.partitionName() == "recovery" {
  // Hardcoded to match make behavior.
  // https://cs.android.com/android/_/android/platform/build/+/2a0ef42a432d4da00201e8eb7697dcaa68fd2389:core/Makefile;l=6957-6962;drc=9ea8ad9232cef4d0a24d70133b1b9d2ce2defe5f;bpv=1;bpt=0
  prefix = ""
 }
 out := android.PathForModuleOut(ctx, "filesystem_config.txt")
 ctx.Build(pctx, android.BuildParams{
  Rule:   fsConfigRule,
  Input:  f.fileystemStagingDirTimestamp(ctx), // assemble the staging directory
  Output: out,
  Args: map[string]string{
   "rootDir": rootDirString,
   "prefix":  prefix,
  },
 })
 return out
}

func (f *filesystem) setVbmetaPartitionProvider(ctx android.ModuleContext) {
 var extractedPublicKey android.ModuleOutPath
 if f.properties.Avb_private_key != nil {
  key := android.PathForModuleSrc(ctx, proptools.String(f.properties.Avb_private_key))
  extractedPublicKey = android.PathForModuleOut(ctx, f.partitionName()+".avbpubkey")
  ctx.Build(pctx, android.BuildParams{
   Rule:   extractPublicKeyRule,
   Input:  key,
   Output: extractedPublicKey,
  })
 }

 var ril int
 if f.properties.Rollback_index_location != nil {
  ril = proptools.Int(f.properties.Rollback_index_location)
 }

 android.SetProvider(ctx, vbmetaPartitionProvider, vbmetaPartitionInfo{
  Name:                  f.partitionName(),
  RollbackIndexLocation: ril,
  PublicKey:             extractedPublicKey,
  Output:                f.output,
 })
}

func (f *filesystem) validateVintfFragments(ctx android.ModuleContext) {
 visitedModule := map[string]bool{}
 packagingSpecs := f.gatherFilteredPackagingSpecs(ctx)

 moduleInFileSystem := func(mod android.ModuleProxy) bool {
  for _, ps := range android.GetInstallFiles(
   ctx, mod).PackagingSpecs {
   if _, ok := packagingSpecs[ps.RelPathInPackage()]; ok {
    return true
   }
  }
  return false
 }

 ctx.WalkDepsProxy(func(child, parent android.ModuleProxy) bool {
  if visitedModule[child.Name()] {
   return false
  }
  if !moduleInFileSystem(child) {
   visitedModule[child.Name()] = true
   return true
  }

  if installInfo := android.GetInstallFiles(ctx, child); len(installInfo.VintfFragmentsPaths) > 0 {
   ctx.PropertyErrorf(
    "vintf_fragments",
    "Module %s is referenced by soong-defined filesystem %s with property vintf_fragments(%s) in use."+
     " Use vintf_fragment_modules property instead.",
    child.Name(),
    f.BaseModuleName(),
    strings.Join(installInfo.VintfFragmentsPaths.Strings(), ", "),
   )
  }
  visitedModule[child.Name()] = true
  return true
 })
}

func (f *filesystem) appendToEntry(ctx android.ModuleContext, installedFile android.Path) {
 partitionBaseDir := android.PathForModuleOut(ctx, f.rootDirString(), proptools.String(f.properties.Base_dir)).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.VisitDirectDepsProxyWithTag(android.DefaultsDepTag, func(m android.ModuleProxy) {
  if _, ok := android.OtherModuleProvider(ctx, m, FilesystemDefaultsInfoProvider); ok {
   partitionInfo := android.OtherModuleProviderOrDefault(ctx, m, android.PartitionTypeInfoProvider)
   if p.PartitionType() != partitionInfo.PartitionType {
    ctx.PropertyErrorf("partition_type",
     "%s doesn't match with the partition type %s of the filesystem default module %s",
     p.PartitionType(), partitionInfo.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,
 rebasedDir android.OutputPath,
 fullInstallPaths *[]FullInstallPathInfo,
 platformGeneratedFiles *[]string,
) {
 rebasedPrefix, err := filepath.Rel(rootDir.String(), rebasedDir.String())
 if err != nil || strings.HasPrefix(rebasedPrefix, "../") {
  panic("rebasedDir could not be made relative to rootDir")
 }
 if !strings.HasSuffix(rebasedPrefix, "/") {
  rebasedPrefix += "/"
 }
 if rebasedPrefix == "./" {
  rebasedPrefix = ""
 }

 // create dirs and symlinks
 for _, dir := range f.properties.Dirs.GetOrDefault(ctx, nil) {
  // OutputPath.Join verifies dir
  builder.Command().Text("mkdir -p").Text(rootDir.Join(ctx, dir).String())
  // Only add the fullInstallPath logic for files in the rebased dir. The root dir
  // is harder to install to.
  if strings.HasPrefix(dir, rebasedPrefix) {
   *fullInstallPaths = append(*fullInstallPaths, FullInstallPathInfo{
    FullInstallPath: android.PathForModuleInPartitionInstall(ctx, f.PartitionType(), strings.TrimPrefix(dir, rebasedPrefix)),
    IsDir:           true,
   })
  }
 }

 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().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)
  // Add the fullInstallPath logic for files in the rebased dir, and for non-rebased files in "system" partition
  // the fullInstallPath is changed to "root" which aligns to the behavior in Make.
  if f.PartitionType() == "system" {
   installPath := android.PathForModuleInPartitionInstall(ctx, f.PartitionType(), strings.TrimPrefix(name, rebasedPrefix))
   if !strings.HasPrefix(name, rebasedPrefix) {
    installPath = android.PathForModuleInPartitionInstall(ctx, "root", name)
   }
   *platformGeneratedFiles = append(*platformGeneratedFiles, installPath.String())
   *fullInstallPaths = append(*fullInstallPaths, FullInstallPathInfo{
    FullInstallPath: installPath,
    SymlinkTarget:   target,
   })
  } else {
   if strings.HasPrefix(name, rebasedPrefix) {
    installPath := android.PathForModuleInPartitionInstall(ctx, f.PartitionType(), strings.TrimPrefix(name, rebasedPrefix))
    *fullInstallPaths = append(*fullInstallPaths, FullInstallPathInfo{
     FullInstallPath: installPath,
     SymlinkTarget:   target,
    })
    *platformGeneratedFiles = append(*platformGeneratedFiles, installPath.String())
   }
  }
 }

 // https://cs.android.com/android/platform/superproject/main/+/main:build/make/core/Makefile;l=2835;drc=b186569ef00ff2f2a1fab28aedc75ebc32bcd67b
 if f.partitionName() == "recovery" {
  builder.Command().Text("mkdir -p").Text(rootDir.Join(ctx, "root/linkerconfig").String())
  builder.Command().Text("touch").Text(rootDir.Join(ctx, "root/linkerconfig/ld.config.txt").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)

 for rel, spec := range specs {
  if spec.Partition() == "root" {
   rootDirSpecs[rel] = spec
  } else {
   rebasedDirSpecs[rel] = spec
  }
 }

 dirsToSpecs := make(map[android.WritablePath]map[string]android.PackagingSpec)
 dirsToSpecs[rootDir] = rootDirSpecs
 dirsToSpecs[rebasedDir] = rebasedDirSpecs

 // Preserve timestamps for adb sync, so that this staging dir file matches the timestamp in the
 // out/target/product staging directory.
 return f.CopySpecsToDirs(ctx, builder, dirsToSpecs, true)
}

func (f *filesystem) rootDirString() string {
 return f.partitionName()
}

func (f *filesystem) verifyGenericConfig(ctx android.ModuleContext) {
 // This image is not bundled with the platform.
 if ctx.Config().UnbundledBuild() {
  return
 }

 // Verify that modules installed in the system partition use the generic configiguration. This
 // also checks there are any unexpected dependencies from system modules to modules installed in
 // non-system partitions.
 if !f.UseGenericConfig() || f.partitionName() != "system" || proptools.Bool(f.properties.Is_auto_generated) {
  return
 }

 allowedModules := []string{
  // build_flag_system collects information from the metadata for each product.
  "build_flag_system",
  // system_ext may have bootjars that are non-generic.
  "dex_bootjars",
  // microdroid_ramdisk is an android_filesystem included in the system image.
  "microdroid_ramdisk",
  // notice_xml_system collects information from the metadata for each product.
  "notice_xml_system",
  // product_config collects all product variables that are required in every partition.
  "product_config",
 }

 nonGenericModules := make(map[string]string)
 visitedModules := make(map[string]bool)

 for _, m := range allowedModules {
  visitedModules[m] = true
 }

 ctx.WalkDepsProxy(func(child, parent android.ModuleProxy) bool {
  moduleName := child.Name()
  if visitedModules[moduleName] {
   return false
  }
  visitedModules[moduleName] = true

  moduleInfo := android.OtherModulePointerProviderOrDefault(ctx, child, android.CommonModuleInfoProvider)
  if !moduleInfo.Enabled || moduleInfo.Target.Os.Class == android.Host {
   return false
  }

  // Dynamic dependencies can be installed in a non-system partition
  isDynamicDependency := false
  depTag := ctx.OtherModuleDependencyTag(child)
  if java.IsOptionalUsesLibraryDepTag(depTag) || java.IsLibDepTag(depTag) || java.IsTraceReferencesDepTag(depTag) || cc.IsSharedDepTag(depTag) {
   isDynamicDependency = true
  }

  // Non-generic config is allowed only for the modules installed in non-system partitions.
  // The dependency must be from a dynamic link or system subpartition modules.
  isAllowedNonGenericConfig := false
  if moduleInfo.SystemExtSpecific {
   isAllowedNonGenericConfig = isDynamicDependency || android.InList("system_ext", f.subPartitions)
  } else if moduleInfo.ProductSpecific {
   isAllowedNonGenericConfig = isDynamicDependency || android.InList("product", f.subPartitions)
  } else if moduleInfo.Vendor || moduleInfo.Proprietary || moduleInfo.SocSpecific {
   isAllowedNonGenericConfig = isDynamicDependency || android.InList("vendor", f.subPartitions)
  }

  // Modules requiring non-generic configuration must not be included in the system image.
  // However, some targets install system_ext or product modules in the system partition.
  // Allow those subpartition modules to use non-generic configuration.
  if !moduleInfo.UseGenericConfig {
   if !isAllowedNonGenericConfig {
    nonGenericModules[moduleName] = parent.Name()
   }
   return false
  }
  return true
 })

 if len(nonGenericModules) > 0 {
  errStr := "\n"
  for _, m := range android.SortedKeys(nonGenericModules) {
   errStr += fmt.Sprintf("\t%q from %q,\n", m, nonGenericModules[m])
  }
  ctx.ModuleErrorf("includes non-generic modules:%s", errStr)
 }
}

type buildImageParams struct {
 // inputs
 rootDir  android.OutputPath
 propFile android.Path
 toolDeps android.Paths
 // outputs
 output android.WritablePath
}

func (f *filesystem) buildImageUsingBuildImage(
 ctx android.ModuleContext,
 builder *android.RuleBuilder,
 params buildImageParams) {

 if f.RunHostInitVerifierCheck() {
  // run host_init_verifier
  // Ideally we should have a concept of pluggable linters that verify the generated image.
  // While such concept is not implement this will do.
  // TODO(b/263574231): substitute with pluggable linter.
  builder.Command().
   BuiltTool("host_init_verifier").
   FlagWithArg("--out_system=", params.rootDir.String())
 }

 // Most of the time, if build_image were to call a host tool, it accepts the path to the
 // host tool in a field in the prop file. However, it doesn't have that option for fec, which
 // it expects to just be on the PATH. Add fec to the PATH.
 fec := ctx.Config().HostToolPath(ctx, "fec")
 pathToolDirs := []string{filepath.Dir(fec.String())}

 builder.Command().
  Textf("PATH=%s:$PATH", strings.Join(pathToolDirs, ":")).
  BuiltTool("build_image").
  Text(params.rootDir.String()). // input directory
  Input(params.propFile).
  Implicits(params.toolDeps).
  Implicit(fec).
  Implicit(f.fileystemStagingDirTimestamp(ctx)). // assemble the staging directory
  Output(params.output).
  Text(params.rootDir.String()) // directory where to find fs_config_files|dirs

 if f.properties.Partition_size != nil {
  assertMaxImageSize(builder, params.output, *f.properties.Partition_size, false)
 }

 // rootDir is not deleted. Might be useful for quick inspection.
 builder.Build("build_"+params.output.String(), fmt.Sprintf("Creating filesystem %s", f.BaseModuleName()))
}

func (f *filesystem) buildFileContexts(ctx android.ModuleContext) android.Path {
 builder := android.NewRuleBuilder(pctx, ctx).SandboxDisabled()
 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
}

// https://source.corp.google.com/h/googleplex-android/platform/build/+/15aeee21e69003e63b64c1dc678cb09b6bcc335d:tools/releasetools/build_image.py;l=745-756;drc=9a3d812e3cff4d9005fa1d4991dfa059e5d2a319;bpv=1;bpt=0
var ro_mount_points = []string{
 "odm",
 "odm_dlkm",
 "oem",
 "product",
 "system",
 "system_dlkm",
 "system_ext",
 "system_other",
 "vendor",
 "vendor_dlkm",
}

func (f *filesystem) buildPropFile(ctx android.ModuleContext) (android.Path, android.Paths) {
 var deps android.Paths
 var lines []string
 addStr := func(name string, value string) {
  lines = append(lines, fmt.Sprintf("%s=%s", name, value))
 }
 addPath := func(name string, path android.Path) {
  addStr(name, path.String())
  deps = append(deps, path)
 }

 addStr("fs_type", f.fsType(ctx).String())
 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.
 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)
  if f.properties.Avb_private_key != nil {
   key := android.PathForModuleSrc(ctx, *f.properties.Avb_private_key)
   addPath("avb_key_path", key)
  }
  addStr("partition_name", f.partitionName())
  avbAddHashTreeFooterArgs, avbAddHashTreeFooterDeps := f.getAvbAddHashtreeFooterArgs(ctx)
  addStr("avb_add_hashtree_footer_args", avbAddHashTreeFooterArgs)
  deps = append(deps, avbAddHashTreeFooterDeps...)
 }

 if f.properties.File_contexts != nil && f.properties.Precompiled_file_contexts != nil {
  ctx.ModuleErrorf("file_contexts and precompiled_file_contexts cannot both be set")
 } else if f.properties.File_contexts != nil {
  f.selinuxFc = f.buildFileContexts(ctx)
 } else if f.properties.Precompiled_file_contexts != nil {
  f.selinuxFc = android.PathForModuleSrc(ctx, *f.properties.Precompiled_file_contexts)
 }
 if f.selinuxFc != nil {
  addPath("selinux_fc", f.selinuxFc)
 }
 if timestamp := proptools.String(f.properties.Fake_timestamp); timestamp != "" {
  addStr("timestamp", timestamp)
 } else if ctx.Config().Getenv("USE_FIXED_TIMESTAMP_IMG_FILES") == "true" {
  addStr("use_fixed_timestamp", "true")
 } else if !proptools.Bool(f.properties.No_use_fixed_timestamp) {
  addStr("use_fixed_timestamp", "true")
 }

 if uuid := proptools.String(f.properties.Uuid); uuid != "" {
  addStr("uuid", uuid)
  addStr("hash_seed", uuid)
 }

 // Disable sparse only when partition size is not defined. disable_sparse has the same
 // effect as <partition name>_disable_sparse.
 if f.properties.Partition_size == nil {
  addStr("disable_sparse", "true")
 }

 fst := f.fsType(ctx)
 switch fst {
 case erofsType:
  // Add erofs properties
  addStr("erofs_default_compressor", f.properties.Erofs.Compressor.GetOrDefault(ctx, "lz4hc,9"))
  if v := f.properties.Erofs.Compress_hints.Get(ctx); v.IsPresent() {
   src := android.PathForModuleSrc(ctx, v.Get())
   addPath("erofs_default_compress_hints", src)
  }
  if proptools.BoolDefault(f.properties.Erofs.Sparse, true) {
   // https://source.corp.google.com/h/googleplex-android/platform/build/+/88b1c67239ca545b11580237242774b411f2fed9:core/Makefile;l=2292;bpv=1;bpt=0;drc=ea8f34bc1d6e63656b4ec32f2391e9d54b3ebb6b
   addStr("erofs_sparse_flag", "-s")
  }

  if f.properties.Erofs.Pcluster_size != nil {
   addStr("erofs_pcluster_size", fmt.Sprintf("%d", *f.properties.Erofs.Pcluster_size))
  }

  if f.properties.Erofs.Block_size != nil {
   addStr("erofs_blocksize", fmt.Sprintf("%d", *f.properties.Erofs.Block_size))
  }

  if proptools.Bool(f.properties.Erofs.Enable_dedupe) {
   addStr("erofs_enable_dedupe", "true")
  }

 case f2fsType:
  if proptools.BoolDefault(f.properties.F2fs.Sparse, true) {
   // https://source.corp.google.com/h/googleplex-android/platform/build/+/88b1c67239ca545b11580237242774b411f2fed9:core/Makefile;l=2294;drc=ea8f34bc1d6e63656b4ec32f2391e9d54b3ebb6b;bpv=1;bpt=0
   addStr("f2fs_sparse_flag", "-S")
  }
  if f.properties.F2fs_blocksize != nil {
   addStr("f2fs_blocksize", strconv.FormatInt(*f.properties.F2fs_blocksize, 10))
  }
  if proptools.BoolDefault(f.properties.Support_f2fs_packedssa, false) {
   addStr("f2fs_packed_ssa", "1")
  }

 case ext4Type:
  // build_image.py implicitly adds some properties when mount point is not specified.
  // Make built prop files does not specify mount_point, but Soong built prop files do.
  // This causes some binary diffs in the final .img files.
  // To fix this, explicitly set these properties in the prop file.
  // https://source.corp.google.com/h/googleplex-android/platform/build/+/15aeee21e69003e63b64c1dc678cb09b6bcc335d:tools/releasetools/build_image.py;l=817-826;drc=9a3d812e3cff4d9005fa1d4991dfa059e5d2a319;bpv=0;bpt=0
  if android.InList(f.PartitionType(), ro_mount_points) {
   addStr("extfs_rsv_pct", "0")
   addStr("journal_size", "0")
   addStr("ro_mount_point", "1")
  }
 }

 f.checkFsTypePropertyError(ctx, fst, fst.String())

 if f.properties.Partition_size != nil {
  addStr("partition_size", strconv.FormatInt(*f.properties.Partition_size, 10))
 }

 if proptools.BoolDefault(f.properties.Support_casefolding, false) {
  addStr("needs_casefold", "1")
 }

 if proptools.BoolDefault(f.properties.Support_project_quota, false) {
  addStr("needs_projid", "1")
 }

 if proptools.BoolDefault(f.properties.Enable_compression, false) {
  addStr("needs_compress", "1")
 }

 if proptools.Bool(f.properties.Share_dup_blocks) {
  if f.fsType(ctx) != erofsType && f.fsType(ctx) != ext4Type {
   ctx.ModuleErrorf("Share_dup_blocks is not supprorted for fs type: %s\n", f.fsType(ctx))
  }
  addStr(f.fsType(ctx).String()+"_share_dup_blocks", "true")
 }

 sort.Strings(lines)

 propFilePreProcessing := android.PathForModuleOut(ctx, "prop_pre_processing")
 android.WriteFileRule(ctx, propFilePreProcessing, strings.Join(lines, "\n"))
 propFile := android.PathForModuleOut(ctx, "prop")
 ctx.Build(pctx, android.BuildParams{
  Rule:   textFileProcessorRule,
  Input:  propFilePreProcessing,
  Output: propFile,
 })
 return propFile, deps
}

func (f *filesystem) buildPropFileForMiscInfo(ctx android.ModuleContext) android.Path {
 var lines []string
 addStr := func(name string, value string) {
  lines = append(lines, fmt.Sprintf("%s=%s", name, value))
 }
 var deps android.Paths

 addStr("use_dynamic_partition_size", "true")
 addStr("ext_mkuserimg", "mkuserimg_mke2fs")

 addStr("building_"+f.partitionName()+"_image", "true")
 addStr(f.partitionName()+"_fs_type", f.fsType(ctx).String())

 if proptools.Bool(f.properties.Share_dup_blocks) {
  addStr(f.fsType(ctx).String()+"_share_dup_blocks", "true")
 }

 if proptools.Bool(f.properties.Use_avb) {
  addStr("avb_"+f.partitionName()+"_hashtree_enable", "true")
  avbAddHashTreeFooterArgs, avbAddHashTreeFooterDeps := f.getAvbAddHashtreeFooterArgs(ctx)
  addStr("avb_"+f.partitionName()+"_add_hashtree_footer_args", strings.TrimSpace(avbAddHashTreeFooterArgs))
  deps = append(deps, avbAddHashTreeFooterDeps...)
 }

 if f.selinuxFc != nil {
  addStr(f.partitionName()+"_selinux_fc", f.selinuxFc.String())
 }

 // Disable sparse only when partition size is not defined. disable_sparse has the same
 // effect as <partition name>_disable_sparse.
 if f.properties.Partition_size == nil {
  addStr(f.partitionName()+"_disable_sparse", "true")
 } else if f.partitionName() == "userdata" {
  // Add userdata's partition size to misc_info.txt.
  // userdata has been special-cased to make the make packaging misc_info.txt implementation
  addStr("userdata_size", strconv.FormatInt(*f.properties.Partition_size, 10))
 }

 fst := f.fsType(ctx)
 switch fst {
 case erofsType:
  // Add erofs properties
  addStr("erofs_default_compressor", f.properties.Erofs.Compressor.GetOrDefault(ctx, "lz4hc,9"))
  if proptools.BoolDefault(f.properties.Erofs.Sparse, true) {
   // https://source.corp.google.com/h/googleplex-android/platform/build/+/88b1c67239ca545b11580237242774b411f2fed9:core/Makefile;l=2292;bpv=1;bpt=0;drc=ea8f34bc1d6e63656b4ec32f2391e9d54b3ebb6b
   addStr("erofs_sparse_flag", "-s")
  }

  if f.properties.Erofs.Pcluster_size != nil {
   addStr(f.partitionName()+"_erofs_pcluster_size", fmt.Sprintf("%d", *f.properties.Erofs.Pcluster_size))
  }

  if f.properties.Erofs.Block_size != nil {
   addStr(f.partitionName()+"_erofs_blocksize", fmt.Sprintf("%d", *f.properties.Erofs.Block_size))
  }
  if proptools.Bool(f.properties.Erofs.Enable_dedupe) {
   addStr("erofs_enable_dedupe", "true")
  }

 case f2fsType:
  if proptools.BoolDefault(f.properties.F2fs.Sparse, true) {
   // https://source.corp.google.com/h/googleplex-android/platform/build/+/88b1c67239ca545b11580237242774b411f2fed9:core/Makefile;l=2294;drc=ea8f34bc1d6e63656b4ec32f2391e9d54b3ebb6b;bpv=1;bpt=0
   addStr("f2fs_sparse_flag", "-S")
  }
  if f.properties.F2fs_blocksize != nil {
   addStr("f2fs_blocksize", strconv.FormatInt(*f.properties.F2fs_blocksize, 10))
  }
  if proptools.BoolDefault(f.properties.Support_f2fs_packedssa, false) {
   addStr("f2fs_packed_ssa", "1")
  }
 }

 if proptools.BoolDefault(f.properties.Support_casefolding, false) {
  addStr("needs_casefold", "1")
 }

 if proptools.BoolDefault(f.properties.Support_project_quota, false) {
  addStr("needs_projid", "1")
 }

 if proptools.BoolDefault(f.properties.Enable_compression, false) {
  addStr("needs_compress", "1")
 }

 sort.Strings(lines)

 propFilePreProcessing := android.PathForModuleOut(ctx, "prop_misc_info_pre_processing")
 android.WriteFileRule(ctx, propFilePreProcessing, strings.Join(lines, "\n"))
 propFile := android.PathForModuleOut(ctx, "prop_file_for_misc_info")
 ctx.Build(pctx, android.BuildParams{
  Rule:      textFileProcessorRule,
  Input:     propFilePreProcessing,
  Output:    propFile,
  Implicits: deps,
 })

 return propFile
}

// Returns the avb_add_hashtree_footer_args value for this module. This value depends on the
// contents of some other files, which will be returned as the second return value, and must
// be tracked as implicit deps. The result should also be passed through textFileProcessorRule
// to read the files and incorporate the results into the args.
func (f *filesystem) getAvbAddHashtreeFooterArgs(ctx android.ModuleContext) (string, android.Paths) {
 var deps android.Paths
 avb_add_hashtree_footer_args := ""
 if !proptools.BoolDefault(f.properties.Use_fec, true) {
  avb_add_hashtree_footer_args += " --do_not_generate_fec"
 }
 hashAlgorithm := proptools.StringDefault(f.properties.Avb_hash_algorithm, "sha256")
 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)
 }
 avb_add_hashtree_footer_args += fmt.Sprintf(" --prop com.android.build.%s.os_version:%s", f.partitionName(), ctx.Config().PlatformVersionLastStable())
 // We're not going to add BuildFingerPrintFile as a dep. If it changed, it's likely because
 // the build number changed, and we don't want to trigger rebuilds solely based on the build
 // number.
 if f.partitionName() == "system" {
  // The system partition has product-agnostic fingerprint.
  avb_add_hashtree_footer_args += fmt.Sprintf(" --prop com.android.build.%s.fingerprint:{CONTENTS_OF:%s}", f.partitionName(), ctx.Config().BuildSystemFingerprintFile(ctx))
  deps = append(deps, ctx.Config().BuildSystemFingerprintFile(ctx))
 } else if ctx.Module().UseGenericConfig() {
  // If non-system partition wants to use generic config, use thumbprint instead.
  avb_add_hashtree_footer_args += fmt.Sprintf(" --prop com.android.build.%s.fingerprint:{CONTENTS_OF:%s}", f.partitionName(), ctx.Config().BuildThumbprintFile(ctx))
  deps = append(deps, ctx.Config().BuildThumbprintFile(ctx))
 } else {
  avb_add_hashtree_footer_args += fmt.Sprintf(" --prop com.android.build.%s.fingerprint:{CONTENTS_OF:%s}", f.partitionName(), ctx.Config().BuildFingerprintFile(ctx))
  deps = append(deps, ctx.Config().BuildFingerprintFile(ctx))
 }
 if f.properties.Security_patch != nil && proptools.String(f.properties.Security_patch) != "" {
  avb_add_hashtree_footer_args += fmt.Sprintf(" --prop com.android.build.%s.security_patch:%s", f.partitionName(), proptools.String(f.properties.Security_patch))
 } else if android.InList(f.partitionName(), []string{"system", "system_ext", "product"}) {
  avb_add_hashtree_footer_args += fmt.Sprintf(" --prop com.android.build.%s.security_patch:%s", f.partitionName(), ctx.Config().PlatformSecurityPatch())
 }
 return avb_add_hashtree_footer_args, deps
}

// This method checks if there is any property set for the fstype(s) other than
// the current fstype.
func (f *filesystem) checkFsTypePropertyError(ctx android.ModuleContext, t fsType, fs string) {
 raiseError := func(otherFsType, currentFsType string) {
  errMsg := fmt.Sprintf("%s is non-empty, but FS type is %s\n. Please delete %s properties if this partition should use %s\n", otherFsType, currentFsType, otherFsType, currentFsType)
  ctx.PropertyErrorf(otherFsType, errMsg)
 }

 if t != erofsType {
  compressor := f.properties.Erofs.Compressor.Get(ctx)
  compressHints := f.properties.Erofs.Compress_hints.Get(ctx)
  if compressor.IsPresent() || compressHints.IsPresent() || f.properties.Erofs.Sparse != nil {
   raiseError("erofs", fs)
  }
 }
 if t != f2fsType {
  if f.properties.F2fs.Sparse != nil {
   raiseError("f2fs", fs)
  }
 }
}

func includeFilesRootDir(ctx android.ModuleContext) (rootDirs android.Paths, partitions android.Paths) {
 ctx.VisitDirectDepsProxyWithTag(interPartitionInstallDependencyTag, func(m android.ModuleProxy) {
  if fsProvider, ok := android.OtherModuleProvider(ctx, m, FilesystemProvider); ok {
   rootDirs = append(rootDirs, fsProvider.RootDir)
   partitions = append(partitions, fsProvider.Output)
  } else {
   ctx.PropertyErrorf("include_files_of", "only filesystem modules can be listed in "+
    "include_files_of but %s is not a filesystem module", m.Name())
  }
 })
 return rootDirs, partitions
}

func includeFilesInstalledFiles(ctx android.ModuleContext) (ret []depset.DepSet[InstalledFilesStruct]) {
 ctx.VisitDirectDepsProxyWithTag(interPartitionInstallDependencyTag, func(m android.ModuleProxy) {
  if fsProvider, ok := android.OtherModuleProvider(ctx, m, FilesystemProvider); ok {
   ret = append(ret, fsProvider.InstalledFilesDepSet)
  }
 })
 return
}

func (f *filesystem) hasOrIsRecovery(ctx android.ModuleContext) bool {
 if f.partitionName() == "recovery" {
  return true
 }
 ret := false
 ctx.VisitDirectDepsProxyWithTag(interPartitionInstallDependencyTag, func(m android.ModuleProxy) {
  if fsProvider, ok := android.OtherModuleProvider(ctx, m, FilesystemProvider); ok && fsProvider.PartitionName == "recovery" {
   ret = true
  }
 })
 return ret
}

func (f *filesystem) buildCpioImage(
 ctx android.ModuleContext,
 builder *android.RuleBuilder,
 rootDir android.OutputPath,
 compressed bool,
) (android.Path, android.Paths) {
 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.")
 }

 rootDirs, partitions := includeFilesRootDir(ctx)

 output := android.PathForModuleOut(ctx, f.installFileName())
 cmd := builder.Command().
  BuiltTool("mkbootfs").
  Implicit(f.fileystemStagingDirTimestamp(ctx)).
  Text(rootDir.String()) // input directory

 for i := range len(rootDirs) {
  cmd.Text(rootDirs[i].String())
 }
 cmd.Implicits(partitions)

 if nodeList := f.properties.Dev_nodes_description_file; nodeList != nil {
  cmd.FlagWithInput("-n ", android.PathForModuleSrc(ctx, proptools.String(nodeList)))
 }
 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, rootDirs
}

var validPartitions = []string{
 "system",
 "userdata",
 "cache",
 "system_other",
 "vendor",
 "product",
 "system_ext",
 "odm",
 "vendor_dlkm",
 "odm_dlkm",
 "system_dlkm",
 "ramdisk",
 "vendor_ramdisk",
 "recovery",
 "vendor_kernel_ramdisk",
 "vendor_ramdisk-debug",
 "vendor_ramdisk-test-harness",
 "debug_ramdisk",
 "test_harness_ramdisk",
}

func (f *filesystem) buildEventLogtagsFile(
 ctx android.ModuleContext,
 builder *android.RuleBuilder,
 rebasedDir android.OutputPath,
 fullInstallPaths *[]FullInstallPathInfo,
 platformGeneratedFiles *[]string,
) {
 if !proptools.Bool(f.properties.Build_logtags) {
  return
 }

 etcPath := rebasedDir.Join(ctx, "etc")
 eventLogtagsPath := etcPath.Join(ctx, "event-log-tags")
 builder.Command().Text("mkdir").Flag("-p").Text(etcPath.String())
 builder.Command().Text("cp").Input(android.MergedLogtagsPath(ctx)).Text(eventLogtagsPath.String())

 installPath := android.PathForModuleInPartitionInstall(ctx, f.PartitionType(), "etc", "event-log-tags")
 *fullInstallPaths = append(*fullInstallPaths, FullInstallPathInfo{
  FullInstallPath: installPath,
  SourcePath:      android.MergedLogtagsPath(ctx),
 })

 f.appendToEntry(ctx, eventLogtagsPath)
 *platformGeneratedFiles = append(*platformGeneratedFiles, installPath.String())
}

func (f *filesystem) BuildLinkerConfigFile(
 ctx android.ModuleContext,
 builder *android.RuleBuilder,
 rebasedDir android.OutputPath,
 fullInstallPaths *[]FullInstallPathInfo,
 platformGeneratedFiles *[]string,
) {
 if !proptools.Bool(f.properties.Linker_config.Gen_linker_config) {
  return
 }

 provideModules, _ := f.getLibsForLinkerConfig(ctx)
 intermediateOutput := android.PathForModuleOut(ctx, "linker.config.pb")
 linkerconfig.BuildLinkerConfig(ctx, android.PathsForModuleSrc(ctx, f.properties.Linker_config.Linker_config_srcs), provideModules, nil, intermediateOutput)
 output := rebasedDir.Join(ctx, "etc", "linker.config.pb")
 builder.Command().Text("cp").Input(intermediateOutput).Output(output)

 installPath := android.PathForModuleInPartitionInstall(ctx, f.PartitionType(), "etc", "linker.config.pb")
 *fullInstallPaths = append(*fullInstallPaths, FullInstallPathInfo{
  FullInstallPath: installPath,
  SourcePath:      intermediateOutput,
 })
 *platformGeneratedFiles = append(*platformGeneratedFiles, installPath.String())

 f.appendToEntry(ctx, output)
}

// Runs assemble_vintf on vintf_fragments of installed deps.
// vintf_fragment_modules are excluded, since they have been
// already processed via assemble_vintf.
func (f *filesystem) assembleVintfs(
 ctx android.ModuleContext,
 builder *android.RuleBuilder,
 rebasedDir android.OutputPath,
 fullInstallPaths *[]FullInstallPathInfo,
 platformGeneratedFiles *[]string,
) {

 if len(f.UniqueVintfFragmentsPaths) == 0 {
  return
 }
 pathPrefix := ""
 if f.PartitionType() == "recovery" {
  // https://cs.android.com/android/_/android/platform/build/+/045a3d6a3e359633a14853a5a5e1e4f2a11cbdae:core/base_rules.mk;l=562;drc=41cada8d4e9b91386d9b691c4ae485425ef6a62e;bpv=1;bpt=0
  pathPrefix = "root/system" // vintf manifests are not installed in root.
 }
 vintfInStagingDir := rebasedDir.Join(ctx, pathPrefix, "etc", "vintf", "manifest")
 builder.Command().Text("mkdir").Flag("-p").Text(vintfInStagingDir.String())

 for _, vintf := range f.UniqueVintfFragmentsPaths {
  soongAssembledVintf := android.PathForModuleOut(ctx, "assemble_vintf", vintf.Base())
  ctx.Build(pctx, android.BuildParams{
   Rule:   android.AssembleVintfRule,
   Input:  vintf,
   Output: soongAssembledVintf,
  })
  soongAssembledVintfInStagingDir := vintfInStagingDir.Join(ctx, vintf.Base())
  builder.Command().Text("cp").Input(soongAssembledVintf).Output(soongAssembledVintfInStagingDir)
  f.appendToEntry(ctx, soongAssembledVintfInStagingDir)

  installPath := android.PathForModuleInPartitionInstall(ctx, f.PartitionType(), pathPrefix, "etc", "vintf", "manifest", vintf.Base())
  *fullInstallPaths = append(*fullInstallPaths, FullInstallPathInfo{
   FullInstallPath: installPath,
   SourcePath:      soongAssembledVintf,
  })
  *platformGeneratedFiles = append(*platformGeneratedFiles, installPath.String())
 }
}

func (f *filesystem) ShouldUseVintfFragmentModuleOnly() bool {
 return false
}

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
func (f *filesystem) AndroidMkEntries() []android.AndroidMkEntries {
 return []android.AndroidMkEntries{android.AndroidMkEntries{
  Class:      "ETC",
  OutputFile: android.OptionalPathForPath(f.output),
  ExtraEntries: []android.AndroidMkExtraEntriesFunc{
   func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
    entries.SetString("LOCAL_MODULE_PATH", f.installDir.String())
    entries.SetString("LOCAL_INSTALLED_MODULE_STEM", f.installFileName())
    if f.fileListFile != nil {
     entries.SetString("LOCAL_FILESYSTEM_FILELIST", f.fileListFile.String())
    }
    if f.avbKey != nil {
     entries.SetString("LOCAL_FILESYSTEM_AVB_KEY_PATH", f.avbKey.String())
    }
    entries.SetString("LOCAL_FILESYSTEM_AVB_ALGORITHM", proptools.StringDefault(f.properties.Avb_algorithm, "SHA256_RSA4096"))
    entries.SetBoolIfTrue(fmt.Sprintf("BOARD_%s_IMAGE_NO_FLASHALL", strings.ToUpper(f.Name())), proptools.Bool(f.properties.No_flashall))
   },
  },
 }}
}

// 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
}

// 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
// for symbol lookup by imitating "activated" paths.
func (f *filesystem) gatherFilteredPackagingSpecs(ctx android.ModuleContext) map[string]android.PackagingSpec {
 return f.PackagingBase.GatherPackagingSpecsWithFilterAndModifier(ctx, f.filesystemBuilder.FilterOverriddenModulesTransitivePackagingSpecs, f.filesystemBuilder.FilterPackagingSpec, f.filesystemBuilder.ModifyPackagingSpec)
}

func (f *filesystem) gatherOwners(specs map[string]android.PackagingSpec) []InstalledModuleInfo {
 var owners []InstalledModuleInfo
 for _, p := range android.SortedKeys(specs) {
  spec := specs[p]
  owners = append(owners, InstalledModuleInfo{
   Name:      spec.Owner(),
   Variation: spec.Variation(),
   Prebuilt:  spec.Prebuilt(),
  })
 }
 return owners
}

func (f *filesystem) gatherSubPartitionOwners(ctx android.ModuleContext) (ret []depset.DepSet[InstalledModuleInfo]) {
 ctx.VisitDirectDepsProxyWithTag(interPartitionInstallDependencyTag, func(m android.ModuleProxy) {
  if fsProvider, ok := android.OtherModuleProvider(ctx, m, FilesystemProvider); ok {
   ret = append(ret, fsProvider.Owners)
  }
 })
 return
}

// Dexpreopt files are installed to system_other. Collect the packaingSpecs for the dexpreopt files
// from this partition to export to the system_other partition later.
func (f *filesystem) systemOtherFiles(ctx android.ModuleContext) map[string]android.PackagingSpec {
 filter := func(spec android.PackagingSpec) bool {
  // For some reason system_other packaging specs don't set the partition field.
  return strings.HasPrefix(spec.RelPathInPackage(), "system_other/")
 }
 modifier := func(spec *android.PackagingSpec) {
  spec.SetRelPathInPackage(strings.TrimPrefix(spec.RelPathInPackage(), "system_other/"))
  spec.SetPartition("system_other")
 }
 return f.PackagingBase.GatherPackagingSpecsWithFilterAndModifier(ctx, nil, filter, modifier)
}

func sha1sum(values []string) string {
 h := sha256.New()
 for _, value := range values {
  io.WriteString(h, value)
 }
 return fmt.Sprintf("%x", h.Sum(nil))
}

// Base cc.UseCoverage

var _ cc.UseCoverage = (*filesystem)(nil)

func (*filesystem) IsNativeCoverageNeeded(ctx cc.IsNativeCoverageNeededContext) bool {
 return ctx.Device() && ctx.DeviceConfig().NativeCoverageEnabled()
}

// android_filesystem_defaults

type filesystemDefaults struct {
 android.ModuleBase
 android.DefaultsModuleBase

 properties FilesystemProperties
}

// 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)
 android.SetProvider(ctx, FilesystemDefaultsInfoProvider, FilesystemDefaultsInfo{})
 android.SetProvider(ctx, android.PartitionTypeInfoProvider, android.PartitionTypeInfo{
  PartitionType: f.PartitionType(),
 })
}

// getLibsForLinkerConfig returns
// 1. A list of libraries installed in this filesystem
// 2. A list of dep libraries _not_ installed in this filesystem
//
// `linkerconfig.BuildLinkerConfig` will convert these two to a linker.config.pb for the filesystem
// (1) will be added to --provideLibs if they are C libraries with a stable interface (has stubs)
// (2) will be added to --requireLibs if they are C libraries with a stable interface (has stubs)
func (f *filesystem) getLibsForLinkerConfig(ctx android.ModuleContext) ([]android.ModuleProxy, []android.ModuleProxy) {
 // we need "Module"s for packaging items
 modulesInPackageByModule := make(map[android.ModuleProxy]bool)
 modulesInPackageByName := make(map[string]bool)

 deps := f.gatherFilteredPackagingSpecs(ctx)
 ctx.WalkDepsProxy(func(child, parent android.ModuleProxy) bool {
  commonInfo := android.OtherModulePointerProviderOrDefault(ctx, child, android.CommonModuleInfoProvider)
  if !commonInfo.Enabled {
   return false
  }
  for _, ps := range android.GetInstallFilesCommon(commonInfo).PackagingSpecs {
   if _, ok := deps[ps.RelPathInPackage()]; ok && ps.Partition() == f.PartitionType() {
    modulesInPackageByModule[child] = true
    modulesInPackageByName[child.Name()] = true
    return true
   }
  }
  return true
 })

 provideModules := make([]android.ModuleProxy, 0, len(modulesInPackageByModule))
 for mod := range modulesInPackageByModule {
  provideModules = append(provideModules, mod)
 }

 var requireModules []android.ModuleProxy
 ctx.WalkDepsProxy(func(child, parent android.ModuleProxy) bool {
  if !android.OtherModulePointerProviderOrDefault(ctx, child, android.CommonModuleInfoProvider).Enabled {
   return false
  }
  _, 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
 })

 return provideModules, requireModules
}

// Checks that the given file doesn't exceed the given size, and will also print a warning
// if it's nearing the maximum size. Equivalent to assert-max-image-size in make:
// https://cs.android.com/android/platform/superproject/main/+/main:build/make/core/definitions.mk;l=3455;drc=993c4de29a02a6accd60ceaaee153307e1a18d10
func assertMaxImageSize(builder *android.RuleBuilder, image android.Path, maxSize int64, addAvbLater bool) {
 if addAvbLater {
  // The value 69632 is derived from MAX_VBMETA_SIZE + MAX_FOOTER_SIZE in avbtool.
  // Logic copied from make:
  // https://cs.android.com/android/platform/superproject/main/+/main:build/make/core/Makefile;l=228;drc=a6a0007ef24e16c0b79f439beac4a118416717e6
  maxSize -= 69632
 }
 cmd := builder.Command()
 cmd.Textf(`file="%s"; maxsize="%d";`+
  `total=$(stat -c "%%s" "$file" | tr -d '\n');`+
  `if [ "$total" -gt "$maxsize" ]; then `+
  `  echo "error: $file too large ($total > $maxsize)";`+
  `  false;`+
  `elif [ "$total" -gt $((maxsize - 32768)) ]; then `+
  `  echo "WARNING: $file approaching size limit ($total now; limit $maxsize)";`+
  `fi`,
  image, maxSize)
 cmd.Implicit(image)
}

// addAutogeneratedRroDeps walks the transitive closure of vendor and product partitions.
// It visits apps installed in system and system_ext partitions, and adds the autogenerated
// RRO modules to its own deps.
func addAutogeneratedRroDeps(ctx android.BottomUpMutatorContext) {
 overlayModuleName := func(child android.ModuleProxy, partition string) string {
  ret := java.AutogeneratedRroModuleName(ctx, child.Name(), partition)
  // Use the fully qualified name if the app is a soong namespace
  if ctx.OtherModuleNamespace(child).Path != "." {
   ret = "//" + ctx.OtherModuleNamespace(child).Path + ":" + ret
  }
  return ret
 }
 productCharacteristicsOverlayModuleName := func(child android.ModuleProxy) string {
  ret := java.AutogeneratedProductCharacteristicsRroModuleName(ctx, child.Name())
  // Use the fully qualified name if the app is a soong namespace
  if ctx.OtherModuleNamespace(child).Path != "." {
   ret = "//" + ctx.OtherModuleNamespace(child).Path + ":" + ret
  }
  return ret
 }

 f, ok := ctx.Module().(*filesystem)
 if !ok {
  return
 }
 thisPartition := f.PartitionType()
 if thisPartition != "vendor" && thisPartition != "product" {
  if f.properties.Android_filesystem_deps.System != nil {
   ctx.PropertyErrorf("android_filesystem_deps.system", "only vendor or product partitions can use android_filesystem_deps")
  }
  if f.properties.Android_filesystem_deps.System_ext != nil {
   ctx.PropertyErrorf("android_filesystem_deps.system_ext", "only vendor or product partitions can use android_filesystem_deps")
  }
  return
 }
 ctx.WalkDepsProxy(func(child, parent android.ModuleProxy) bool {
  depTag := ctx.OtherModuleDependencyTag(child)

  var isAddDepTag bool
  if _, ok := depTag.(WalkAutoGeneratedRroDepsTag); ok {
   isAddDepTag = true
  }

  // Dep walk in fining the auto generated rro deps are limited to the following dep tags:
  // - addDepTag: dependency tag used to add the modules listed as deps in the filesystem module
  // - InstallNeededDependencyTag: install dependency tag
  // - interPartitionDependencyTag: dependency tag used to depend on other filesystem modules
  if !(android.IsInstallDepNeededTag(depTag) || depTag == interPartitionDependencyTag || isAddDepTag) {
   return false
  }

  // Auto generated rros of apps that are installed via apex are not installed in make.
  // Only add the dependencies for non-apex variants.
  if android.OtherModuleProviderOrDefault(ctx, child, android.PlatformAvailabilityInfoProvider).NotAvailableToPlatform {
   return true
  }
  if apexInfo, ok := android.OtherModuleProvider(ctx, child, android.ApexInfoProvider); !ok || apexInfo.ApexVariationName == "" {
   if vendorOverlay := overlayModuleName(child, "vendor"); ctx.OtherModuleExists(vendorOverlay) && thisPartition == "vendor" {
    ctx.AddFarVariationDependencies(nil, dependencyTagWithVisibilityEnforcementBypass, vendorOverlay)
   }
   if productOverlay := overlayModuleName(child, "product"); ctx.OtherModuleExists(productOverlay) && thisPartition == "product" {
    ctx.AddFarVariationDependencies(nil, dependencyTagWithVisibilityEnforcementBypass, productOverlay)
   }
   if productCharacteristics := productCharacteristicsOverlayModuleName(child); ctx.OtherModuleExists(productCharacteristics) && thisPartition == "product" {
    ctx.AddFarVariationDependencies(nil, dependencyTagWithVisibilityEnforcementBypass, productCharacteristics)
   }
  }

  return true
 })
}

func (f *filesystem) MakeVars(ctx android.MakeVarsModuleContext) []android.ModuleMakeVarsValue {
 if f.Name() == ctx.Config().SoongDefinedSystemImage() && android.IsModulePreferredProxy(ctx.(android.OtherModuleProviderContext), f) {
  return []android.ModuleMakeVarsValue{{"SOONG_DEFINED_SYSTEM_IMAGE_PATH", f.output.String()}}
 }
 return nil
}

func setCommonFilesystemInfo(ctx android.ModuleContext, m Filesystem) {
 android.SetProvider(ctx, FilesystemProvider, FilesystemInfo{
  Output:           m.OutputPath(),
  SignedOutputPath: m.SignedOutputPath(),
 })
}

// Runs checkvintf on the staging directory of the filesystem.
func (f *filesystem) checkVintf(ctx android.ModuleContext, rebasedDir android.OutputPath) android.Path {
 if !proptools.Bool(f.properties.Check_vintf) {
  return nil
 }
 checkVintfLog := android.PathForModuleOut(ctx, "vintf", "check_vintf_"+f.PartitionType()+".log")
 extractedApexDir := android.PathForModuleOut(ctx, "vintf", "apex_extracted")

 builder := android.NewRuleBuilder(pctx, ctx).SandboxDisabled()
 // Use apexd_host to extract the apexes of this partition to an intermediate location.
 // This intermediate location will be subsequently used by checkvintf.
 cmd := builder.Command()
 cmd.Textf("rm -rf %s", checkVintfLog).
  Textf("rm -rf %s && mkdir -p %s && ", extractedApexDir.String(), extractedApexDir.String()).
  BuiltTool("apexd_host").
  FlagWithArg(fmt.Sprintf(" --%s_path ", f.PartitionType()), rebasedDir.String()).
  FlagWithArg(" --apex_path ", extractedApexDir.String())

 if f.PartitionType() == "system" {
  cmd.Textf(" && ").
   BuiltTool("checkvintf").
   Flag("--check-one").
   Implicit(f.fileystemStagingDirTimestamp(ctx)).
   FlagWithArg("--dirmap ", fmt.Sprintf("/system:%s", rebasedDir.String())).
   FlagWithArg("--dirmap ", fmt.Sprintf("/apex:%s", extractedApexDir.String())).
   FlagWithOutput(">> ", checkVintfLog).
   Flag("2>&1 ").
   Textf(" || ( cat %s && exit 1 ); ", checkVintfLog.String())
 } else {
  // checkvintf against each device sku
  for _, vendorSku := range deviceSkusForCheckVintf(ctx) {
   cmd.Textf(" && ").
    BuiltTool("checkvintf").
    Flag("--check-one").
    Implicit(f.fileystemStagingDirTimestamp(ctx)).
    FlagWithArg("--dirmap ", fmt.Sprintf("/vendor:%s", rebasedDir.String())).
    FlagWithArg("--dirmap ", fmt.Sprintf("/apex:%s", extractedApexDir.String())).
    FlagWithArg("--property ", "ro.boot.product.vendor.sku="+vendorSku).
    FlagWithOutput(">> ", checkVintfLog).
    Flag("2>&1 ").
    Textf(" || ( cat %s && exit 1 ); ", checkVintfLog.String())
  }
 }

 builder.Build(checkVintfLog.Base(), checkVintfLog.Base())
 return checkVintfLog
}

func deviceSkusForCheckVintf(ctx android.ModuleContext) []string {
 // Check vendor SKU=(empty) case when:
 // - DEVICE_MANIFEST_FILE is not empty; OR
 // - DEVICE_MANIFEST_FILE is empty AND DEVICE_MANIFEST_SKUS is empty (only vendor manifest fragments are used)
 if len(ctx.Config().DeviceManifestFiles()) > 0 {
  return []string{""}
 } else if len(ctx.Config().DeviceManifestSkus()) == 0 {
  return []string{""}
 }
 return ctx.Config().DeviceManifestSkus()
}

func odmSkusForCheckVintf(ctx android.ModuleContext) []string {
 // Check ODM SKU=(empty) case when:
 // - ODM_MANIFEST_FILE is not empty; OR
 // - ODM_MANIFEST_FILE is empty AND ODM_MANIFEST_SKUS is empty (only vendor manifest fragments are used)
 if len(ctx.Config().OdmManifestFiles()) > 0 {
  return []string{""}
 } else if len(ctx.Config().OdmManifestSkus()) == 0 {
  return []string{""}
 }
 return ctx.Config().OdmManifestSkus()
}

[Dauer der Verarbeitung: 0.37 Sekunden, vorverarbeitet 2026-06-28]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik