1// Copyright (C) 2020 The Android Open Source Project 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15package filesystem 16 17import ( 18 "crypto/sha256" 19 "fmt" 20 "io" 21 "path/filepath" 22 "slices" 23 "strconv" 24 "strings" 25 26 "android/soong/android" 27 "android/soong/cc" 28 "android/soong/java" 29 "android/soong/linkerconfig" 30 31 "github.com/google/blueprint" 32 "github.com/google/blueprint/proptools" 33) 34 35func init() { 36 registerBuildComponents(android.InitRegistrationContext) 37 registerMutators(android.InitRegistrationContext) 38} 39 40func registerBuildComponents(ctx android.RegistrationContext) { 41 ctx.RegisterModuleType("android_filesystem", FilesystemFactory) 42 ctx.RegisterModuleType("android_filesystem_defaults", filesystemDefaultsFactory) 43 ctx.RegisterModuleType("android_system_image", SystemImageFactory) 44 ctx.RegisterModuleType("avb_add_hash_footer", avbAddHashFooterFactory) 45 ctx.RegisterModuleType("avb_add_hash_footer_defaults", avbAddHashFooterDefaultsFactory) 46 ctx.RegisterModuleType("avb_gen_vbmeta_image", avbGenVbmetaImageFactory) 47 ctx.RegisterModuleType("avb_gen_vbmeta_image_defaults", avbGenVbmetaImageDefaultsFactory) 48} 49 50func registerMutators(ctx android.RegistrationContext) { 51 ctx.PostDepsMutators(func(ctx android.RegisterMutatorsContext) { 52 ctx.BottomUp("add_autogenerated_rro_deps", addAutogeneratedRroDeps) 53 }) 54} 55 56// Remember to add referenced files to implicits! 57var textFileProcessorRule = pctx.AndroidStaticRule("text_file_processing", blueprint.RuleParams{ 58 Command: "build/soong/scripts/text_file_processor.py $in $out", 59 CommandDeps: []string{"build/soong/scripts/text_file_processor.py"}, 60}) 61 62type filesystem struct { 63 android.ModuleBase 64 android.PackagingBase 65 android.DefaultableModuleBase 66 67 properties FilesystemProperties 68 69 output android.Path 70 installDir android.InstallPath 71 72 fileListFile android.Path 73 74 // Keeps the entries installed from this filesystem 75 entries []string 76 77 filesystemBuilder filesystemBuilder 78} 79 80type filesystemBuilder interface { 81 BuildLinkerConfigFile(ctx android.ModuleContext, builder *android.RuleBuilder, rebasedDir android.OutputPath) 82 // Function that filters PackagingSpec in PackagingBase.GatherPackagingSpecs() 83 FilterPackagingSpec(spec android.PackagingSpec) bool 84 // Function that modifies PackagingSpec in PackagingBase.GatherPackagingSpecs() to customize. 85 // For example, GSI system.img contains system_ext and product artifacts and their 86 // relPathInPackage need to be rebased to system/system_ext and system/system_product. 87 ModifyPackagingSpec(spec *android.PackagingSpec) 88 89 // Function to check if the filesystem should not use `vintf_fragments` property, 90 // but use `vintf_fragment` module type instead 91 ShouldUseVintfFragmentModuleOnly() bool 92} 93 94var _ filesystemBuilder = (*filesystem)(nil) 95 96type SymlinkDefinition struct { 97 Target *string 98 Name *string 99} 100 101type FilesystemProperties struct { 102 // When set to true, sign the image with avbtool. Default is false. 103 Use_avb *bool 104 105 // Path to the private key that avbtool will use to sign this filesystem image. 106 // TODO(jiyong): allow apex_key to be specified here 107 Avb_private_key *string `android:"path"` 108 109 // Signing algorithm for avbtool. Default is SHA256_RSA4096. 110 Avb_algorithm *string 111 112 // Hash algorithm used for avbtool (for descriptors). This is passed as hash_algorithm to 113 // avbtool. Default used by avbtool is sha1. 114 Avb_hash_algorithm *string 115 116 // Whether or not to use forward-error-correction codes when signing with AVB. Defaults to true. 117 Use_fec *bool 118 119 // The index used to prevent rollback of the image. Only used if use_avb is true. 120 Rollback_index *int64 121 122 // Name of the partition stored in vbmeta desc. Defaults to the name of this module. 123 Partition_name *string 124 125 // Type of the filesystem. Currently, ext4, erofs, cpio, and compressed_cpio are supported. Default 126 // is ext4. 127 Type *string 128 129 // Identifies which partition this is for //visibility:any_system_image (and others) visibility 130 // checks, and will be used in the future for API surface checks. 131 Partition_type *string 132 133 // file_contexts file to make image. Currently, only ext4 is supported. These file contexts 134 // will be compiled with sefcontext_compile 135 File_contexts *string `android:"path"` 136 137 // The selinux file contexts, after having already run them through sefcontext_compile 138 Precompiled_file_contexts *string `android:"path"` 139 140 // Base directory relative to root, to which deps are installed, e.g. "system". Default is "." 141 // (root). 142 Base_dir *string 143 144 // Directories to be created under root. e.g. /dev, /proc, etc. 145 Dirs proptools.Configurable[[]string] 146 147 // Symbolic links to be created under root with "ln -sf <target> <name>". 148 Symlinks []SymlinkDefinition 149 150 // Seconds since unix epoch to override timestamps of file entries 151 Fake_timestamp *string 152 153 // When set, passed to mkuserimg_mke2fs --mke2fs_uuid & --mke2fs_hash_seed. 154 // Otherwise, they'll be set as random which might cause indeterministic build output. 155 Uuid *string 156 157 // Mount point for this image. Default is "/" 158 Mount_point *string 159 160 // If set to the name of a partition ("system", "vendor", etc), this filesystem module 161 // will also include the contents of the make-built staging directories. If any soong 162 // modules would be installed to the same location as a make module, they will overwrite 163 // the make version. 164 Include_make_built_files string 165 166 // When set, builds etc/event-log-tags file by merging logtags from all dependencies. 167 // Default is false 168 Build_logtags *bool 169 170 // Install aconfig_flags.pb file for the modules installed in this partition. 171 Gen_aconfig_flags_pb *bool 172 173 Fsverity fsverityProperties 174 175 // If this property is set to true, the filesystem will call ctx.UncheckedModule(), causing 176 // it to not be built on checkbuilds. Used for the automatic migration from make to soong 177 // build modules, where we want to emit some not-yet-working filesystems and we don't want them 178 // to be built. 179 Unchecked_module *bool `blueprint:"mutated"` 180 181 Erofs ErofsProperties 182 183 F2fs F2fsProperties 184 185 Linker_config LinkerConfigProperties 186 187 // Determines if the module is auto-generated from Soong or not. If the module is 188 // auto-generated, its deps are exempted from visibility enforcement. 189 Is_auto_generated *bool 190 191 // Path to the dev nodes description file. This is only needed for building the ramdisk 192 // partition and should not be explicitly specified. 193 Dev_nodes_description_file *string `android:"path" blueprint:"mutated"` 194 195 // Additional dependencies used for building android products 196 Android_filesystem_deps AndroidFilesystemDeps 197} 198 199type AndroidFilesystemDeps struct { 200 System *string 201 System_ext *string 202} 203 204// Additional properties required to generate erofs FS partitions. 205type ErofsProperties struct { 206 // Compressor and Compression level passed to mkfs.erofs. e.g. (lz4hc,9) 207 // Please see external/erofs-utils/README for complete documentation. 208 Compressor *string 209 210 // Used as --compress-hints for mkfs.erofs 211 Compress_hints *string `android:"path"` 212 213 Sparse *bool 214} 215 216// Additional properties required to generate f2fs FS partitions. 217type F2fsProperties struct { 218 Sparse *bool 219} 220 221type LinkerConfigProperties struct { 222 223 // Build a linker.config.pb file 224 Gen_linker_config *bool 225 226 // List of files (in .json format) that will be converted to a linker config file (in .pb format). 227 // The linker config file be installed in the filesystem at /etc/linker.config.pb 228 Linker_config_srcs []string `android:"path"` 229} 230 231// android_filesystem packages a set of modules and their transitive dependencies into a filesystem 232// image. The filesystem images are expected to be mounted in the target device, which means the 233// modules in the filesystem image are built for the target device (i.e. Android, not Linux host). 234// The modules are placed in the filesystem image just like they are installed to the ordinary 235// partitions like system.img. For example, cc_library modules are placed under ./lib[64] directory. 236func FilesystemFactory() android.Module { 237 module := &filesystem{} 238 module.filesystemBuilder = module 239 initFilesystemModule(module, module) 240 return module 241} 242 243func initFilesystemModule(module android.DefaultableModule, filesystemModule *filesystem) { 244 module.AddProperties(&filesystemModule.properties) 245 android.InitPackageModule(filesystemModule) 246 filesystemModule.PackagingBase.DepsCollectFirstTargetOnly = true 247 filesystemModule.PackagingBase.AllowHighPriorityDeps = true 248 android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon) 249 android.InitDefaultableModule(module) 250 251 android.AddLoadHook(module, func(ctx android.LoadHookContext) { 252 filesystemModule.setDevNodesDescriptionProp() 253 }) 254} 255 256type depTag struct { 257 blueprint.BaseDependencyTag 258 android.PackagingItemAlwaysDepTag 259} 260 261var dependencyTag = depTag{} 262 263type depTagWithVisibilityEnforcementBypass struct { 264 depTag 265} 266 267type interPartitionDepTag struct { 268 blueprint.BaseDependencyTag 269} 270 271var interPartitionDependencyTag = interPartitionDepTag{} 272 273var _ android.ExcludeFromVisibilityEnforcementTag = (*depTagWithVisibilityEnforcementBypass)(nil) 274 275func (t depTagWithVisibilityEnforcementBypass) ExcludeFromVisibilityEnforcement() {} 276 277var dependencyTagWithVisibilityEnforcementBypass = depTagWithVisibilityEnforcementBypass{} 278 279// ramdiskDevNodesDescription is the name of the filegroup module that provides the file that 280// contains the description of dev nodes added to the CPIO archive for the ramdisk partition. 281const ramdiskDevNodesDescription = "ramdisk_node_list" 282 283func (f *filesystem) setDevNodesDescriptionProp() { 284 if proptools.String(f.properties.Partition_name) == "ramdisk" { 285 f.properties.Dev_nodes_description_file = proptools.StringPtr(":" + ramdiskDevNodesDescription) 286 } 287} 288 289func (f *filesystem) DepsMutator(ctx android.BottomUpMutatorContext) { 290 if proptools.Bool(f.properties.Is_auto_generated) { 291 f.AddDeps(ctx, dependencyTagWithVisibilityEnforcementBypass) 292 } else { 293 f.AddDeps(ctx, dependencyTag) 294 } 295 if f.properties.Android_filesystem_deps.System != nil { 296 ctx.AddDependency(ctx.Module(), interPartitionDependencyTag, proptools.String(f.properties.Android_filesystem_deps.System)) 297 } 298 if f.properties.Android_filesystem_deps.System_ext != nil { 299 ctx.AddDependency(ctx.Module(), interPartitionDependencyTag, proptools.String(f.properties.Android_filesystem_deps.System_ext)) 300 } 301} 302 303type fsType int 304 305const ( 306 ext4Type fsType = iota 307 erofsType 308 f2fsType 309 compressedCpioType 310 cpioType // uncompressed 311 unknown 312) 313 314func (fs fsType) IsUnknown() bool { 315 return fs == unknown 316} 317 318type FilesystemInfo struct { 319 // A text file containing the list of paths installed on the partition. 320 FileListFile android.Path 321} 322 323var FilesystemProvider = blueprint.NewProvider[FilesystemInfo]() 324 325func GetFsTypeFromString(ctx android.EarlyModuleContext, typeStr string) fsType { 326 switch typeStr { 327 case "ext4": 328 return ext4Type 329 case "erofs": 330 return erofsType 331 case "f2fs": 332 return f2fsType 333 case "compressed_cpio": 334 return compressedCpioType 335 case "cpio": 336 return cpioType 337 default: 338 return unknown 339 } 340} 341 342func (f *filesystem) fsType(ctx android.ModuleContext) fsType { 343 typeStr := proptools.StringDefault(f.properties.Type, "ext4") 344 fsType := GetFsTypeFromString(ctx, typeStr) 345 if fsType == unknown { 346 ctx.PropertyErrorf("type", "%q not supported", typeStr) 347 } 348 return fsType 349} 350 351func (f *filesystem) installFileName() string { 352 return f.BaseModuleName() + ".img" 353} 354 355func (f *filesystem) partitionName() string { 356 return proptools.StringDefault(f.properties.Partition_name, f.Name()) 357} 358 359func (f *filesystem) FilterPackagingSpec(ps android.PackagingSpec) bool { 360 // Filesystem module respects the installation semantic. A PackagingSpec from a module with 361 // IsSkipInstall() is skipped. 362 if ps.SkipInstall() { 363 return false 364 } 365 if proptools.Bool(f.properties.Is_auto_generated) { // TODO (spandandas): Remove this. 366 pt := f.PartitionType() 367 return ps.Partition() == pt || strings.HasPrefix(ps.Partition(), pt+"/") 368 } 369 return true 370} 371 372func (f *filesystem) ModifyPackagingSpec(ps *android.PackagingSpec) { 373 // Sometimes, android.modulePartition() returns a path with >1 path components. 374 // This makes the partition field of packagingSpecs have multiple components, like 375 // "system/product". Right now, the filesystem module doesn't look at the partition field 376 // when deciding what path to install the file under, only the RelPathInPackage field, so 377 // we move the later path components from partition to relPathInPackage. This should probably 378 // be revisited in the future. 379 prefix := f.PartitionType() + "/" 380 if strings.HasPrefix(ps.Partition(), prefix) { 381 subPartition := strings.TrimPrefix(ps.Partition(), prefix) 382 ps.SetPartition(f.PartitionType()) 383 ps.SetRelPathInPackage(filepath.Join(subPartition, ps.RelPathInPackage())) 384 } 385} 386 387var pctx = android.NewPackageContext("android/soong/filesystem") 388 389func (f *filesystem) GenerateAndroidBuildActions(ctx android.ModuleContext) { 390 validatePartitionType(ctx, f) 391 if f.filesystemBuilder.ShouldUseVintfFragmentModuleOnly() { 392 f.validateVintfFragments(ctx) 393 } 394 switch f.fsType(ctx) { 395 case ext4Type, erofsType, f2fsType: 396 f.output = f.buildImageUsingBuildImage(ctx) 397 case compressedCpioType: 398 f.output = f.buildCpioImage(ctx, true) 399 case cpioType: 400 f.output = f.buildCpioImage(ctx, false) 401 default: 402 return 403 } 404 405 f.installDir = android.PathForModuleInstall(ctx, "etc") 406 ctx.InstallFile(f.installDir, f.installFileName(), f.output) 407 ctx.SetOutputFiles([]android.Path{f.output}, "") 408 409 fileListFile := android.PathForModuleOut(ctx, "fileList") 410 android.WriteFileRule(ctx, fileListFile, f.installedFilesList()) 411 412 android.SetProvider(ctx, FilesystemProvider, FilesystemInfo{ 413 FileListFile: fileListFile, 414 }) 415 f.fileListFile = fileListFile 416 417 if proptools.Bool(f.properties.Unchecked_module) { 418 ctx.UncheckedModule() 419 } 420} 421 422func (f *filesystem) validateVintfFragments(ctx android.ModuleContext) { 423 visitedModule := map[string]bool{} 424 packagingSpecs := f.gatherFilteredPackagingSpecs(ctx) 425 426 moduleInFileSystem := func(mod android.Module) bool { 427 for _, ps := range android.OtherModuleProviderOrDefault( 428 ctx, mod, android.InstallFilesProvider).PackagingSpecs { 429 if _, ok := packagingSpecs[ps.RelPathInPackage()]; ok { 430 return true 431 } 432 } 433 return false 434 } 435 436 ctx.WalkDeps(func(child, parent android.Module) bool { 437 if visitedModule[child.Name()] { 438 return false 439 } 440 if !moduleInFileSystem(child) { 441 visitedModule[child.Name()] = true 442 return true 443 } 444 if vintfFragments := child.VintfFragments(ctx); vintfFragments != nil { 445 ctx.PropertyErrorf( 446 "vintf_fragments", 447 "Module %s is referenced by soong-defined filesystem %s with property vintf_fragments(%s) in use."+ 448 " Use vintf_fragment_modules property instead.", 449 child.Name(), 450 f.BaseModuleName(), 451 strings.Join(vintfFragments, ", "), 452 ) 453 } 454 visitedModule[child.Name()] = true 455 return true 456 }) 457} 458 459func (f *filesystem) appendToEntry(ctx android.ModuleContext, installedFile android.Path) { 460 partitionBaseDir := android.PathForModuleOut(ctx, "root", proptools.String(f.properties.Base_dir)).String() + "/" 461 462 relPath, inTargetPartition := strings.CutPrefix(installedFile.String(), partitionBaseDir) 463 if inTargetPartition { 464 f.entries = append(f.entries, relPath) 465 } 466} 467 468func (f *filesystem) installedFilesList() string { 469 installedFilePaths := android.FirstUniqueStrings(f.entries) 470 slices.Sort(installedFilePaths) 471 472 return strings.Join(installedFilePaths, "\n") 473} 474 475func validatePartitionType(ctx android.ModuleContext, p partition) { 476 if !android.InList(p.PartitionType(), validPartitions) { 477 ctx.PropertyErrorf("partition_type", "partition_type must be one of %s, found: %s", validPartitions, p.PartitionType()) 478 } 479 480 ctx.VisitDirectDepsWithTag(android.DefaultsDepTag, func(m android.Module) { 481 if fdm, ok := m.(*filesystemDefaults); ok { 482 if p.PartitionType() != fdm.PartitionType() { 483 ctx.PropertyErrorf("partition_type", 484 "%s doesn't match with the partition type %s of the filesystem default module %s", 485 p.PartitionType(), fdm.PartitionType(), m.Name()) 486 } 487 } 488 }) 489} 490 491// Copy extra files/dirs that are not from the `deps` property to `rootDir`, checking for conflicts with files 492// already in `rootDir`. 493func (f *filesystem) buildNonDepsFiles(ctx android.ModuleContext, builder *android.RuleBuilder, rootDir android.OutputPath) { 494 // create dirs and symlinks 495 for _, dir := range f.properties.Dirs.GetOrDefault(ctx, nil) { 496 // OutputPath.Join verifies dir 497 builder.Command().Text("mkdir -p").Text(rootDir.Join(ctx, dir).String()) 498 } 499 500 for _, symlink := range f.properties.Symlinks { 501 name := strings.TrimSpace(proptools.String(symlink.Name)) 502 target := strings.TrimSpace(proptools.String(symlink.Target)) 503 504 if name == "" { 505 ctx.PropertyErrorf("symlinks", "Name can't be empty") 506 continue 507 } 508 509 if target == "" { 510 ctx.PropertyErrorf("symlinks", "Target can't be empty") 511 continue 512 } 513 514 // OutputPath.Join verifies name. don't need to verify target. 515 dst := rootDir.Join(ctx, name) 516 builder.Command().Textf("(! [ -e %s -o -L %s ] || (echo \"%s already exists from an earlier stage of the build\" && exit 1))", dst, dst, dst) 517 builder.Command().Text("mkdir -p").Text(filepath.Dir(dst.String())) 518 builder.Command().Text("ln -sf").Text(proptools.ShellEscape(target)).Text(dst.String()) 519 f.appendToEntry(ctx, dst) 520 } 521} 522 523func (f *filesystem) copyPackagingSpecs(ctx android.ModuleContext, builder *android.RuleBuilder, specs map[string]android.PackagingSpec, rootDir, rebasedDir android.WritablePath) []string { 524 rootDirSpecs := make(map[string]android.PackagingSpec) 525 rebasedDirSpecs := make(map[string]android.PackagingSpec) 526 527 for rel, spec := range specs { 528 if spec.Partition() == "root" { 529 rootDirSpecs[rel] = spec 530 } else { 531 rebasedDirSpecs[rel] = spec 532 } 533 } 534 535 dirsToSpecs := make(map[android.WritablePath]map[string]android.PackagingSpec) 536 dirsToSpecs[rootDir] = rootDirSpecs 537 dirsToSpecs[rebasedDir] = rebasedDirSpecs 538 539 return f.CopySpecsToDirs(ctx, builder, dirsToSpecs) 540} 541 542func (f *filesystem) copyFilesToProductOut(ctx android.ModuleContext, builder *android.RuleBuilder, rebasedDir android.OutputPath) { 543 if f.Name() != ctx.Config().SoongDefinedSystemImage() { 544 return 545 } 546 installPath := android.PathForModuleInPartitionInstall(ctx, f.partitionName()) 547 builder.Command().Textf("cp -prf %s/* %s", rebasedDir, installPath) 548} 549 550func (f *filesystem) buildImageUsingBuildImage(ctx android.ModuleContext) android.Path { 551 rootDir := android.PathForModuleOut(ctx, "root").OutputPath 552 rebasedDir := rootDir 553 if f.properties.Base_dir != nil { 554 rebasedDir = rootDir.Join(ctx, *f.properties.Base_dir) 555 } 556 builder := android.NewRuleBuilder(pctx, ctx) 557 // Wipe the root dir to get rid of leftover files from prior builds 558 builder.Command().Textf("rm -rf %s && mkdir -p %s", rootDir, rootDir) 559 specs := f.gatherFilteredPackagingSpecs(ctx) 560 f.entries = f.copyPackagingSpecs(ctx, builder, specs, rootDir, rebasedDir) 561 562 f.buildNonDepsFiles(ctx, builder, rootDir) 563 f.addMakeBuiltFiles(ctx, builder, rootDir) 564 f.buildFsverityMetadataFiles(ctx, builder, specs, rootDir, rebasedDir) 565 f.buildEventLogtagsFile(ctx, builder, rebasedDir) 566 f.buildAconfigFlagsFiles(ctx, builder, specs, rebasedDir) 567 f.filesystemBuilder.BuildLinkerConfigFile(ctx, builder, rebasedDir) 568 f.copyFilesToProductOut(ctx, builder, rebasedDir) 569 570 // run host_init_verifier 571 // Ideally we should have a concept of pluggable linters that verify the generated image. 572 // While such concept is not implement this will do. 573 // TODO(b/263574231): substitute with pluggable linter. 574 builder.Command(). 575 BuiltTool("host_init_verifier"). 576 FlagWithArg("--out_system=", rootDir.String()+"/system") 577 578 propFile, toolDeps := f.buildPropFile(ctx) 579 580 // Most of the time, if build_image were to call a host tool, it accepts the path to the 581 // host tool in a field in the prop file. However, it doesn't have that option for fec, which 582 // it expects to just be on the PATH. Add fec to the PATH. 583 fec := ctx.Config().HostToolPath(ctx, "fec") 584 pathToolDirs := []string{filepath.Dir(fec.String())} 585 586 output := android.PathForModuleOut(ctx, f.installFileName()) 587 builder.Command(). 588 Textf("PATH=%s:$PATH", strings.Join(pathToolDirs, ":")). 589 BuiltTool("build_image"). 590 Text(rootDir.String()). // input directory 591 Input(propFile). 592 Implicits(toolDeps). 593 Implicit(fec). 594 Output(output). 595 Text(rootDir.String()) // directory where to find fs_config_files|dirs 596 597 // rootDir is not deleted. Might be useful for quick inspection. 598 builder.Build("build_filesystem_image", fmt.Sprintf("Creating filesystem %s", f.BaseModuleName())) 599 600 return output 601} 602 603func (f *filesystem) buildFileContexts(ctx android.ModuleContext) android.Path { 604 builder := android.NewRuleBuilder(pctx, ctx) 605 fcBin := android.PathForModuleOut(ctx, "file_contexts.bin") 606 builder.Command().BuiltTool("sefcontext_compile"). 607 FlagWithOutput("-o ", fcBin). 608 Input(android.PathForModuleSrc(ctx, proptools.String(f.properties.File_contexts))) 609 builder.Build("build_filesystem_file_contexts", fmt.Sprintf("Creating filesystem file contexts for %s", f.BaseModuleName())) 610 return fcBin 611} 612 613// Calculates avb_salt from entry list (sorted) for deterministic output. 614func (f *filesystem) salt() string { 615 return sha1sum(f.entries) 616} 617 618func (f *filesystem) buildPropFile(ctx android.ModuleContext) (android.Path, android.Paths) { 619 var deps android.Paths 620 var propFileString strings.Builder 621 addStr := func(name string, value string) { 622 propFileString.WriteString(name) 623 propFileString.WriteRune('=') 624 propFileString.WriteString(value) 625 propFileString.WriteRune('\n') 626 } 627 addPath := func(name string, path android.Path) { 628 addStr(name, path.String()) 629 deps = append(deps, path) 630 } 631 632 // Type string that build_image.py accepts. 633 fsTypeStr := func(t fsType) string { 634 switch t { 635 // TODO(372522486): add more types like f2fs, erofs, etc. 636 case ext4Type: 637 return "ext4" 638 case erofsType: 639 return "erofs" 640 case f2fsType: 641 return "f2fs" 642 } 643 panic(fmt.Errorf("unsupported fs type %v", t)) 644 } 645 646 addStr("fs_type", fsTypeStr(f.fsType(ctx))) 647 addStr("mount_point", proptools.StringDefault(f.properties.Mount_point, "/")) 648 addStr("use_dynamic_partition_size", "true") 649 addPath("ext_mkuserimg", ctx.Config().HostToolPath(ctx, "mkuserimg_mke2fs")) 650 // b/177813163 deps of the host tools have to be added. Remove this. 651 for _, t := range []string{"mke2fs", "e2fsdroid", "tune2fs"} { 652 deps = append(deps, ctx.Config().HostToolPath(ctx, t)) 653 } 654 655 if proptools.Bool(f.properties.Use_avb) { 656 addStr("avb_hashtree_enable", "true") 657 addPath("avb_avbtool", ctx.Config().HostToolPath(ctx, "avbtool")) 658 algorithm := proptools.StringDefault(f.properties.Avb_algorithm, "SHA256_RSA4096") 659 addStr("avb_algorithm", algorithm) 660 if f.properties.Avb_private_key != nil { 661 key := android.PathForModuleSrc(ctx, *f.properties.Avb_private_key) 662 addPath("avb_key_path", key) 663 } 664 addStr("partition_name", f.partitionName()) 665 avb_add_hashtree_footer_args := "" 666 if !proptools.BoolDefault(f.properties.Use_fec, true) { 667 avb_add_hashtree_footer_args += " --do_not_generate_fec" 668 } 669 if hashAlgorithm := proptools.String(f.properties.Avb_hash_algorithm); hashAlgorithm != "" { 670 avb_add_hashtree_footer_args += " --hash_algorithm " + hashAlgorithm 671 } 672 if f.properties.Rollback_index != nil { 673 rollbackIndex := proptools.Int(f.properties.Rollback_index) 674 if rollbackIndex < 0 { 675 ctx.PropertyErrorf("rollback_index", "Rollback index must be non-negative") 676 } 677 avb_add_hashtree_footer_args += " --rollback_index " + strconv.Itoa(rollbackIndex) 678 } 679 avb_add_hashtree_footer_args += fmt.Sprintf(" --prop com.android.build.%s.os_version:%s", f.partitionName(), ctx.Config().PlatformVersionLastStable()) 680 avb_add_hashtree_footer_args += fmt.Sprintf(" --prop com.android.build.%s.fingerprint:{CONTENTS_OF:%s}", f.partitionName(), ctx.Config().BuildFingerprintFile(ctx)) 681 avb_add_hashtree_footer_args += fmt.Sprintf(" --prop com.android.build.%s.security_patch:%s", f.partitionName(), ctx.Config().PlatformSecurityPatch()) 682 addStr("avb_add_hashtree_footer_args", avb_add_hashtree_footer_args) 683 addStr("avb_salt", f.salt()) 684 } 685 686 if f.properties.File_contexts != nil && f.properties.Precompiled_file_contexts != nil { 687 ctx.ModuleErrorf("file_contexts and precompiled_file_contexts cannot both be set") 688 } else if f.properties.File_contexts != nil { 689 addPath("selinux_fc", f.buildFileContexts(ctx)) 690 } else if f.properties.Precompiled_file_contexts != nil { 691 src := android.PathForModuleSrc(ctx, *f.properties.Precompiled_file_contexts) 692 if src != nil { 693 addPath("selinux_fc", src) 694 } 695 } 696 if timestamp := proptools.String(f.properties.Fake_timestamp); timestamp != "" { 697 addStr("timestamp", timestamp) 698 } 699 if uuid := proptools.String(f.properties.Uuid); uuid != "" { 700 addStr("uuid", uuid) 701 addStr("hash_seed", uuid) 702 } 703 704 // TODO(b/381120092): This should only be added if none of the size-related properties are set, 705 // but currently soong built partitions don't have size properties. Make code: 706 // https://cs.android.com/android/platform/superproject/main/+/main:build/make/core/Makefile;l=2262;drc=39cd33701c9278db0e7e481a090605f428d5b12d 707 // Make uses system_disable_sparse but disable_sparse has the same effect, and we shouldn't need 708 // to qualify it because each partition gets its own property file built. 709 addStr("disable_sparse", "true") 710 711 fst := f.fsType(ctx) 712 switch fst { 713 case erofsType: 714 // Add erofs properties 715 addStr("erofs_default_compressor", proptools.StringDefault(f.properties.Erofs.Compressor, "lz4hc,9")) 716 if f.properties.Erofs.Compress_hints != nil { 717 src := android.PathForModuleSrc(ctx, *f.properties.Erofs.Compress_hints) 718 addPath("erofs_default_compress_hints", src) 719 } 720 if proptools.BoolDefault(f.properties.Erofs.Sparse, true) { 721 // https://source.corp.google.com/h/googleplex-android/platform/build/+/88b1c67239ca545b11580237242774b411f2fed9:core/Makefile;l=2292;bpv=1;bpt=0;drc=ea8f34bc1d6e63656b4ec32f2391e9d54b3ebb6b 722 addStr("erofs_sparse_flag", "-s") 723 } 724 case f2fsType: 725 if proptools.BoolDefault(f.properties.F2fs.Sparse, true) { 726 // https://source.corp.google.com/h/googleplex-android/platform/build/+/88b1c67239ca545b11580237242774b411f2fed9:core/Makefile;l=2294;drc=ea8f34bc1d6e63656b4ec32f2391e9d54b3ebb6b;bpv=1;bpt=0 727 addStr("f2fs_sparse_flag", "-S") 728 } 729 } 730 f.checkFsTypePropertyError(ctx, fst, fsTypeStr(fst)) 731 732 propFilePreProcessing := android.PathForModuleOut(ctx, "prop_pre_processing") 733 android.WriteFileRuleVerbatim(ctx, propFilePreProcessing, propFileString.String()) 734 propFile := android.PathForModuleOut(ctx, "prop") 735 ctx.Build(pctx, android.BuildParams{ 736 Rule: textFileProcessorRule, 737 Input: propFilePreProcessing, 738 Output: propFile, 739 Implicit: ctx.Config().BuildFingerprintFile(ctx), 740 }) 741 return propFile, deps 742} 743 744// This method checks if there is any property set for the fstype(s) other than 745// the current fstype. 746func (f *filesystem) checkFsTypePropertyError(ctx android.ModuleContext, t fsType, fs string) { 747 raiseError := func(otherFsType, currentFsType string) { 748 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) 749 ctx.PropertyErrorf(otherFsType, errMsg) 750 } 751 752 if t != erofsType { 753 if f.properties.Erofs.Compressor != nil || f.properties.Erofs.Compress_hints != nil || f.properties.Erofs.Sparse != nil { 754 raiseError("erofs", fs) 755 } 756 } 757 if t != f2fsType { 758 if f.properties.F2fs.Sparse != nil { 759 raiseError("f2fs", fs) 760 } 761 } 762} 763 764func (f *filesystem) buildCpioImage(ctx android.ModuleContext, compressed bool) android.Path { 765 if proptools.Bool(f.properties.Use_avb) { 766 ctx.PropertyErrorf("use_avb", "signing compresed cpio image using avbtool is not supported."+ 767 "Consider adding this to bootimg module and signing the entire boot image.") 768 } 769 770 if proptools.String(f.properties.File_contexts) != "" { 771 ctx.PropertyErrorf("file_contexts", "file_contexts is not supported for compressed cpio image.") 772 } 773 774 if f.properties.Include_make_built_files != "" { 775 ctx.PropertyErrorf("include_make_built_files", "include_make_built_files is not supported for compressed cpio image.") 776 } 777 778 rootDir := android.PathForModuleOut(ctx, "root").OutputPath 779 rebasedDir := rootDir 780 if f.properties.Base_dir != nil { 781 rebasedDir = rootDir.Join(ctx, *f.properties.Base_dir) 782 } 783 builder := android.NewRuleBuilder(pctx, ctx) 784 // Wipe the root dir to get rid of leftover files from prior builds 785 builder.Command().Textf("rm -rf %s && mkdir -p %s", rootDir, rootDir) 786 specs := f.gatherFilteredPackagingSpecs(ctx) 787 f.entries = f.copyPackagingSpecs(ctx, builder, specs, rootDir, rebasedDir) 788 789 f.buildNonDepsFiles(ctx, builder, rootDir) 790 f.buildFsverityMetadataFiles(ctx, builder, specs, rootDir, rebasedDir) 791 f.buildEventLogtagsFile(ctx, builder, rebasedDir) 792 f.buildAconfigFlagsFiles(ctx, builder, specs, rebasedDir) 793 f.filesystemBuilder.BuildLinkerConfigFile(ctx, builder, rebasedDir) 794 f.copyFilesToProductOut(ctx, builder, rebasedDir) 795 796 output := android.PathForModuleOut(ctx, f.installFileName()) 797 cmd := builder.Command(). 798 BuiltTool("mkbootfs"). 799 Text(rootDir.String()) // input directory 800 if nodeList := f.properties.Dev_nodes_description_file; nodeList != nil { 801 cmd.FlagWithInput("-n ", android.PathForModuleSrc(ctx, proptools.String(nodeList))) 802 } 803 if compressed { 804 cmd.Text("|"). 805 BuiltTool("lz4"). 806 Flag("--favor-decSpeed"). // for faster boot 807 Flag("-12"). // maximum compression level 808 Flag("-l"). // legacy format for kernel 809 Text(">").Output(output) 810 } else { 811 cmd.Text(">").Output(output) 812 } 813 814 // rootDir is not deleted. Might be useful for quick inspection. 815 builder.Build("build_cpio_image", fmt.Sprintf("Creating filesystem %s", f.BaseModuleName())) 816 817 return output 818} 819 820var validPartitions = []string{ 821 "system", 822 "userdata", 823 "cache", 824 "system_other", 825 "vendor", 826 "product", 827 "system_ext", 828 "odm", 829 "vendor_dlkm", 830 "odm_dlkm", 831 "system_dlkm", 832 "ramdisk", 833 "vendor_ramdisk", 834 "recovery", 835} 836 837func (f *filesystem) addMakeBuiltFiles(ctx android.ModuleContext, builder *android.RuleBuilder, rootDir android.Path) { 838 partition := f.properties.Include_make_built_files 839 if partition == "" { 840 return 841 } 842 if !slices.Contains(validPartitions, partition) { 843 ctx.PropertyErrorf("include_make_built_files", "Expected one of %#v, found %q", validPartitions, partition) 844 return 845 } 846 stampFile := fmt.Sprintf("target/product/%s/obj/PACKAGING/%s_intermediates/staging_dir.stamp", ctx.Config().DeviceName(), partition) 847 fileListFile := fmt.Sprintf("target/product/%s/obj/PACKAGING/%s_intermediates/file_list.txt", ctx.Config().DeviceName(), partition) 848 stagingDir := fmt.Sprintf("target/product/%s/%s", ctx.Config().DeviceName(), partition) 849 850 builder.Command().BuiltTool("merge_directories"). 851 Implicit(android.PathForArbitraryOutput(ctx, stampFile)). 852 Text("--ignore-duplicates"). 853 FlagWithInput("--file-list", android.PathForArbitraryOutput(ctx, fileListFile)). 854 Text(rootDir.String()). 855 Text(android.PathForArbitraryOutput(ctx, stagingDir).String()) 856} 857 858func (f *filesystem) buildEventLogtagsFile(ctx android.ModuleContext, builder *android.RuleBuilder, rebasedDir android.OutputPath) { 859 if !proptools.Bool(f.properties.Build_logtags) { 860 return 861 } 862 863 logtagsFilePaths := make(map[string]bool) 864 ctx.WalkDeps(func(child, parent android.Module) bool { 865 if logtagsInfo, ok := android.OtherModuleProvider(ctx, child, android.LogtagsProviderKey); ok { 866 for _, path := range logtagsInfo.Logtags { 867 logtagsFilePaths[path.String()] = true 868 } 869 } 870 return true 871 }) 872 873 if len(logtagsFilePaths) == 0 { 874 return 875 } 876 877 etcPath := rebasedDir.Join(ctx, "etc") 878 eventLogtagsPath := etcPath.Join(ctx, "event-log-tags") 879 builder.Command().Text("mkdir").Flag("-p").Text(etcPath.String()) 880 cmd := builder.Command().BuiltTool("merge-event-log-tags"). 881 FlagWithArg("-o ", eventLogtagsPath.String()). 882 FlagWithInput("-m ", android.MergedLogtagsPath(ctx)) 883 884 for _, path := range android.SortedKeys(logtagsFilePaths) { 885 cmd.Text(path) 886 } 887 888 f.appendToEntry(ctx, eventLogtagsPath) 889} 890 891func (f *filesystem) BuildLinkerConfigFile(ctx android.ModuleContext, builder *android.RuleBuilder, rebasedDir android.OutputPath) { 892 if !proptools.Bool(f.properties.Linker_config.Gen_linker_config) { 893 return 894 } 895 896 provideModules, _ := f.getLibsForLinkerConfig(ctx) 897 output := rebasedDir.Join(ctx, "etc", "linker.config.pb") 898 linkerconfig.BuildLinkerConfig(ctx, builder, android.PathsForModuleSrc(ctx, f.properties.Linker_config.Linker_config_srcs), provideModules, nil, output) 899 900 f.appendToEntry(ctx, output) 901} 902 903func (f *filesystem) ShouldUseVintfFragmentModuleOnly() bool { 904 return false 905} 906 907type partition interface { 908 PartitionType() string 909} 910 911func (f *filesystem) PartitionType() string { 912 return proptools.StringDefault(f.properties.Partition_type, "system") 913} 914 915var _ partition = (*filesystem)(nil) 916 917var _ android.AndroidMkEntriesProvider = (*filesystem)(nil) 918 919// Implements android.AndroidMkEntriesProvider 920func (f *filesystem) AndroidMkEntries() []android.AndroidMkEntries { 921 return []android.AndroidMkEntries{android.AndroidMkEntries{ 922 Class: "ETC", 923 OutputFile: android.OptionalPathForPath(f.output), 924 ExtraEntries: []android.AndroidMkExtraEntriesFunc{ 925 func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) { 926 entries.SetString("LOCAL_MODULE_PATH", f.installDir.String()) 927 entries.SetString("LOCAL_INSTALLED_MODULE_STEM", f.installFileName()) 928 entries.SetString("LOCAL_FILESYSTEM_FILELIST", f.fileListFile.String()) 929 }, 930 }, 931 }} 932} 933 934// Filesystem is the public interface for the filesystem struct. Currently, it's only for the apex 935// package to have access to the output file. 936type Filesystem interface { 937 android.Module 938 OutputPath() android.Path 939 940 // Returns the output file that is signed by avbtool. If this module is not signed, returns 941 // nil. 942 SignedOutputPath() android.Path 943} 944 945var _ Filesystem = (*filesystem)(nil) 946 947func (f *filesystem) OutputPath() android.Path { 948 return f.output 949} 950 951func (f *filesystem) SignedOutputPath() android.Path { 952 if proptools.Bool(f.properties.Use_avb) { 953 return f.OutputPath() 954 } 955 return nil 956} 957 958// Filter the result of GatherPackagingSpecs to discard items targeting outside "system" partition. 959// Note that "apex" module installs its contents to "apex"(fake partition) as well 960// for symbol lookup by imitating "activated" paths. 961func (f *filesystem) gatherFilteredPackagingSpecs(ctx android.ModuleContext) map[string]android.PackagingSpec { 962 specs := f.PackagingBase.GatherPackagingSpecsWithFilterAndModifier(ctx, f.filesystemBuilder.FilterPackagingSpec, f.filesystemBuilder.ModifyPackagingSpec) 963 return specs 964} 965 966func sha1sum(values []string) string { 967 h := sha256.New() 968 for _, value := range values { 969 io.WriteString(h, value) 970 } 971 return fmt.Sprintf("%x", h.Sum(nil)) 972} 973 974// Base cc.UseCoverage 975 976var _ cc.UseCoverage = (*filesystem)(nil) 977 978func (*filesystem) IsNativeCoverageNeeded(ctx cc.IsNativeCoverageNeededContext) bool { 979 return ctx.Device() && ctx.DeviceConfig().NativeCoverageEnabled() 980} 981 982// android_filesystem_defaults 983 984type filesystemDefaults struct { 985 android.ModuleBase 986 android.DefaultsModuleBase 987 988 properties FilesystemProperties 989} 990 991// android_filesystem_defaults is a default module for android_filesystem and android_system_image 992func filesystemDefaultsFactory() android.Module { 993 module := &filesystemDefaults{} 994 module.AddProperties(&module.properties) 995 module.AddProperties(&android.PackagingProperties{}) 996 android.InitDefaultsModule(module) 997 return module 998} 999 1000func (f *filesystemDefaults) PartitionType() string { 1001 return proptools.StringDefault(f.properties.Partition_type, "system") 1002} 1003 1004var _ partition = (*filesystemDefaults)(nil) 1005 1006func (f *filesystemDefaults) GenerateAndroidBuildActions(ctx android.ModuleContext) { 1007 validatePartitionType(ctx, f) 1008} 1009 1010// getLibsForLinkerConfig returns 1011// 1. A list of libraries installed in this filesystem 1012// 2. A list of dep libraries _not_ installed in this filesystem 1013// 1014// `linkerconfig.BuildLinkerConfig` will convert these two to a linker.config.pb for the filesystem 1015// (1) will be added to --provideLibs if they are C libraries with a stable interface (has stubs) 1016// (2) will be added to --requireLibs if they are C libraries with a stable interface (has stubs) 1017func (f *filesystem) getLibsForLinkerConfig(ctx android.ModuleContext) ([]android.Module, []android.Module) { 1018 // we need "Module"s for packaging items 1019 modulesInPackageByModule := make(map[android.Module]bool) 1020 modulesInPackageByName := make(map[string]bool) 1021 1022 deps := f.gatherFilteredPackagingSpecs(ctx) 1023 ctx.WalkDeps(func(child, parent android.Module) bool { 1024 for _, ps := range android.OtherModuleProviderOrDefault( 1025 ctx, child, android.InstallFilesProvider).PackagingSpecs { 1026 if _, ok := deps[ps.RelPathInPackage()]; ok && ps.Partition() == f.PartitionType() { 1027 modulesInPackageByModule[child] = true 1028 modulesInPackageByName[child.Name()] = true 1029 return true 1030 } 1031 } 1032 return true 1033 }) 1034 1035 provideModules := make([]android.Module, 0, len(modulesInPackageByModule)) 1036 for mod := range modulesInPackageByModule { 1037 provideModules = append(provideModules, mod) 1038 } 1039 1040 var requireModules []android.Module 1041 ctx.WalkDeps(func(child, parent android.Module) bool { 1042 _, parentInPackage := modulesInPackageByModule[parent] 1043 _, childInPackageName := modulesInPackageByName[child.Name()] 1044 1045 // When parent is in the package, and child (or its variant) is not, this can be from an interface. 1046 if parentInPackage && !childInPackageName { 1047 requireModules = append(requireModules, child) 1048 } 1049 return true 1050 }) 1051 1052 return provideModules, requireModules 1053} 1054 1055// Checks that the given file doesn't exceed the given size, and will also print a warning 1056// if it's nearing the maximum size. Equivalent to assert-max-image-size in make: 1057// https://cs.android.com/android/platform/superproject/main/+/main:build/make/core/definitions.mk;l=3455;drc=993c4de29a02a6accd60ceaaee153307e1a18d10 1058func assertMaxImageSize(builder *android.RuleBuilder, image android.Path, maxSize int64, addAvbLater bool) { 1059 if addAvbLater { 1060 // The value 69632 is derived from MAX_VBMETA_SIZE + MAX_FOOTER_SIZE in avbtool. 1061 // Logic copied from make: 1062 // https://cs.android.com/android/platform/superproject/main/+/main:build/make/core/Makefile;l=228;drc=a6a0007ef24e16c0b79f439beac4a118416717e6 1063 maxSize -= 69632 1064 } 1065 cmd := builder.Command() 1066 cmd.Textf(`file="%s"; maxsize="%d";`+ 1067 `total=$(stat -c "%%s" "$file" | tr -d '\n');`+ 1068 `if [ "$total" -gt "$maxsize" ]; then `+ 1069 ` echo "error: $file too large ($total > $maxsize)";`+ 1070 ` false;`+ 1071 `elif [ "$total" -gt $((maxsize - 32768)) ]; then `+ 1072 ` echo "WARNING: $file approaching size limit ($total now; limit $maxsize)";`+ 1073 `fi`, 1074 image, maxSize) 1075 cmd.Implicit(image) 1076} 1077 1078// addAutogeneratedRroDeps walks the transitive closure of vendor and product partitions. 1079// It visits apps installed in system and system_ext partitions, and adds the autogenerated 1080// RRO modules to its own deps. 1081func addAutogeneratedRroDeps(ctx android.BottomUpMutatorContext) { 1082 f, ok := ctx.Module().(*filesystem) 1083 if !ok { 1084 return 1085 } 1086 thisPartition := f.PartitionType() 1087 if thisPartition != "vendor" && thisPartition != "product" { 1088 return 1089 } 1090 ctx.WalkDeps(func(child, parent android.Module) bool { 1091 depTag := ctx.OtherModuleDependencyTag(child) 1092 if parent.Name() == f.Name() && depTag != interPartitionDependencyTag { 1093 return false // This is a module listed in deps of vendor/product filesystem 1094 } 1095 if vendorOverlay := java.AutogeneratedRroModuleName(ctx, child.Name(), "vendor"); ctx.OtherModuleExists(vendorOverlay) && thisPartition == "vendor" { 1096 ctx.AddFarVariationDependencies(nil, dependencyTagWithVisibilityEnforcementBypass, vendorOverlay) 1097 } 1098 if productOverlay := java.AutogeneratedRroModuleName(ctx, child.Name(), "product"); ctx.OtherModuleExists(productOverlay) && thisPartition == "product" { 1099 ctx.AddFarVariationDependencies(nil, dependencyTagWithVisibilityEnforcementBypass, productOverlay) 1100 } 1101 return true 1102 }) 1103} 1104