Define additional symlinks and dirs for recovery partition

To match that of the make generated recovery partition.

Test: mount Soong generated recovery partition and compare with TARGET_RECOVERY_ROOT_OUT
Bug: 381888358
Change-Id: I39822027088895525ef473ad9797f410d45ec7cd
diff --git a/filesystem/filesystem.go b/filesystem/filesystem.go
index b9cb076..7cd5b72 100644
--- a/filesystem/filesystem.go
+++ b/filesystem/filesystem.go
@@ -98,6 +98,14 @@
 	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
diff --git a/fsgen/Android.bp b/fsgen/Android.bp
index 365d954..1b828c5 100644
--- a/fsgen/Android.bp
+++ b/fsgen/Android.bp
@@ -14,6 +14,7 @@
     ],
     srcs: [
         "boot_imgs.go",
+        "config.go",
         "filesystem_creator.go",
         "fsgen_mutators.go",
         "prebuilt_etc_modules_gen.go",
diff --git a/fsgen/config.go b/fsgen/config.go
new file mode 100644
index 0000000..31f721b
--- /dev/null
+++ b/fsgen/config.go
@@ -0,0 +1,142 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package fsgen
+
+import (
+	"android/soong/filesystem"
+
+	"github.com/google/blueprint/proptools"
+)
+
+var (
+	// Most of the symlinks and directories listed here originate from create_root_structure.mk,
+	// but the handwritten generic system image also recreates them:
+	// https://cs.android.com/android/platform/superproject/main/+/main:build/make/target/product/generic/Android.bp;l=33;drc=db08311f1b6ef6cb0a4fbcc6263b89849360ce04
+	// TODO(b/377734331): only generate the symlinks if the relevant partitions exist
+	commonSymlinksFromRoot = []filesystem.SymlinkDefinition{
+		{
+			Target: proptools.StringPtr("/system/bin/init"),
+			Name:   proptools.StringPtr("init"),
+		},
+		{
+			Target: proptools.StringPtr("/system/etc"),
+			Name:   proptools.StringPtr("etc"),
+		},
+		{
+			Target: proptools.StringPtr("/system/bin"),
+			Name:   proptools.StringPtr("bin"),
+		},
+		{
+			Target: proptools.StringPtr("/data/user_de/0/com.android.shell/files/bugreports"),
+			Name:   proptools.StringPtr("bugreports"),
+		},
+		{
+			Target: proptools.StringPtr("/sys/kernel/debug"),
+			Name:   proptools.StringPtr("d"),
+		},
+		{
+			Target: proptools.StringPtr("/product/etc/security/adb_keys"),
+			Name:   proptools.StringPtr("adb_keys"),
+		},
+		{
+			Target: proptools.StringPtr("/vendor/odm/app"),
+			Name:   proptools.StringPtr("odm/app"),
+		},
+		{
+			Target: proptools.StringPtr("/vendor/odm/bin"),
+			Name:   proptools.StringPtr("odm/bin"),
+		},
+		{
+			Target: proptools.StringPtr("/vendor/odm/etc"),
+			Name:   proptools.StringPtr("odm/etc"),
+		},
+		{
+			Target: proptools.StringPtr("/vendor/odm/firmware"),
+			Name:   proptools.StringPtr("odm/firmware"),
+		},
+		{
+			Target: proptools.StringPtr("/vendor/odm/framework"),
+			Name:   proptools.StringPtr("odm/framework"),
+		},
+		{
+			Target: proptools.StringPtr("/vendor/odm/lib"),
+			Name:   proptools.StringPtr("odm/lib"),
+		},
+		{
+			Target: proptools.StringPtr("/vendor/odm/lib64"),
+			Name:   proptools.StringPtr("odm/lib64"),
+		},
+		{
+			Target: proptools.StringPtr("/vendor/odm/overlay"),
+			Name:   proptools.StringPtr("odm/overlay"),
+		},
+		{
+			Target: proptools.StringPtr("/vendor/odm/priv-app"),
+			Name:   proptools.StringPtr("odm/priv-app"),
+		},
+		{
+			Target: proptools.StringPtr("/vendor/odm/usr"),
+			Name:   proptools.StringPtr("odm/usr"),
+		},
+		// For Treble Generic System Image (GSI), system-as-root GSI needs to work on
+		// both devices with and without /odm_dlkm partition. Those symlinks are for
+		// devices without /odm_dlkm partition. For devices with /odm_dlkm
+		// partition, mount odm_dlkm.img under /odm_dlkm will hide those symlinks.
+		// Note that /odm_dlkm/lib is omitted because odm DLKMs should be accessed
+		// via /odm/lib/modules directly. All of this also applies to the vendor_dlkm symlink
+		{
+			Target: proptools.StringPtr("/odm/odm_dlkm/etc"),
+			Name:   proptools.StringPtr("odm_dlkm/etc"),
+		},
+		{
+			Target: proptools.StringPtr("/vendor/vendor_dlkm/etc"),
+			Name:   proptools.StringPtr("vendor_dlkm/etc"),
+		},
+	}
+
+	// Common directories between partitions that may be listed as `Dirs` property in the
+	// filesystem module.
+	commonPartitionDirs = []string{
+		// From generic_rootdirs in build/make/target/product/generic/Android.bp
+		"acct",
+		"apex",
+		"bootstrap-apex",
+		"config",
+		"data",
+		"data_mirror",
+		"debug_ramdisk",
+		"dev",
+		"linkerconfig",
+		"metadata",
+		"mnt",
+		"odm",
+		"odm_dlkm",
+		"oem",
+		"postinstall",
+		"proc",
+		"second_stage_resources",
+		"storage",
+		"sys",
+		"system",
+		"system_dlkm",
+		"tmp",
+		"vendor",
+		"vendor_dlkm",
+
+		// from android_rootdirs in build/make/target/product/generic/Android.bp
+		"system_ext",
+		"product",
+	}
+)
diff --git a/fsgen/filesystem_creator.go b/fsgen/filesystem_creator.go
index 1378513..774320f 100644
--- a/fsgen/filesystem_creator.go
+++ b/fsgen/filesystem_creator.go
@@ -235,146 +235,37 @@
 			}
 			fsProps.Fsverity.Libs = []string{":framework-res{.export-package.apk}"}
 		}
-		// Most of the symlinks and directories listed here originate from create_root_structure.mk,
-		// but the handwritten generic system image also recreates them:
-		// https://cs.android.com/android/platform/superproject/main/+/main:build/make/target/product/generic/Android.bp;l=33;drc=db08311f1b6ef6cb0a4fbcc6263b89849360ce04
-		// TODO(b/377734331): only generate the symlinks if the relevant partitions exist
-		fsProps.Symlinks = []filesystem.SymlinkDefinition{
-			filesystem.SymlinkDefinition{
-				Target: proptools.StringPtr("/system/bin/init"),
-				Name:   proptools.StringPtr("init"),
-			},
-			filesystem.SymlinkDefinition{
-				Target: proptools.StringPtr("/system/etc"),
-				Name:   proptools.StringPtr("etc"),
-			},
-			filesystem.SymlinkDefinition{
-				Target: proptools.StringPtr("/system/bin"),
-				Name:   proptools.StringPtr("bin"),
-			},
-			filesystem.SymlinkDefinition{
-				Target: proptools.StringPtr("/data/user_de/0/com.android.shell/files/bugreports"),
-				Name:   proptools.StringPtr("bugreports"),
-			},
-			filesystem.SymlinkDefinition{
-				Target: proptools.StringPtr("/sys/kernel/debug"),
-				Name:   proptools.StringPtr("d"),
-			},
-			filesystem.SymlinkDefinition{
-				Target: proptools.StringPtr("/storage/self/primary"),
-				Name:   proptools.StringPtr("sdcard"),
-			},
-			filesystem.SymlinkDefinition{
-				Target: proptools.StringPtr("/product/etc/security/adb_keys"),
-				Name:   proptools.StringPtr("adb_keys"),
-			},
-			filesystem.SymlinkDefinition{
-				Target: proptools.StringPtr("/vendor/odm/app"),
-				Name:   proptools.StringPtr("odm/app"),
-			},
-			filesystem.SymlinkDefinition{
-				Target: proptools.StringPtr("/vendor/odm/bin"),
-				Name:   proptools.StringPtr("odm/bin"),
-			},
-			filesystem.SymlinkDefinition{
-				Target: proptools.StringPtr("/vendor/odm/etc"),
-				Name:   proptools.StringPtr("odm/etc"),
-			},
-			filesystem.SymlinkDefinition{
-				Target: proptools.StringPtr("/vendor/odm/firmware"),
-				Name:   proptools.StringPtr("odm/firmware"),
-			},
-			filesystem.SymlinkDefinition{
-				Target: proptools.StringPtr("/vendor/odm/framework"),
-				Name:   proptools.StringPtr("odm/framework"),
-			},
-			filesystem.SymlinkDefinition{
-				Target: proptools.StringPtr("/vendor/odm/lib"),
-				Name:   proptools.StringPtr("odm/lib"),
-			},
-			filesystem.SymlinkDefinition{
-				Target: proptools.StringPtr("/vendor/odm/lib64"),
-				Name:   proptools.StringPtr("odm/lib64"),
-			},
-			filesystem.SymlinkDefinition{
-				Target: proptools.StringPtr("/vendor/odm/overlay"),
-				Name:   proptools.StringPtr("odm/overlay"),
-			},
-			filesystem.SymlinkDefinition{
-				Target: proptools.StringPtr("/vendor/odm/priv-app"),
-				Name:   proptools.StringPtr("odm/priv-app"),
-			},
-			filesystem.SymlinkDefinition{
-				Target: proptools.StringPtr("/vendor/odm/usr"),
-				Name:   proptools.StringPtr("odm/usr"),
-			},
-			filesystem.SymlinkDefinition{
-				Target: proptools.StringPtr("/product"),
-				Name:   proptools.StringPtr("system/product"),
-			},
-			filesystem.SymlinkDefinition{
-				Target: proptools.StringPtr("/system_ext"),
-				Name:   proptools.StringPtr("system/system_ext"),
-			},
-			filesystem.SymlinkDefinition{
-				Target: proptools.StringPtr("/vendor"),
-				Name:   proptools.StringPtr("system/vendor"),
-			},
-			filesystem.SymlinkDefinition{
-				Target: proptools.StringPtr("/system_dlkm/lib/modules"),
-				Name:   proptools.StringPtr("system/lib/modules"),
-			},
-			filesystem.SymlinkDefinition{
-				Target: proptools.StringPtr("/data/cache"),
-				Name:   proptools.StringPtr("cache"),
-			},
-			// For Treble Generic System Image (GSI), system-as-root GSI needs to work on
-			// both devices with and without /odm_dlkm partition. Those symlinks are for
-			// devices without /odm_dlkm partition. For devices with /odm_dlkm
-			// partition, mount odm_dlkm.img under /odm_dlkm will hide those symlinks.
-			// Note that /odm_dlkm/lib is omitted because odm DLKMs should be accessed
-			// via /odm/lib/modules directly. All of this also applies to the vendor_dlkm symlink
-			filesystem.SymlinkDefinition{
-				Target: proptools.StringPtr("/odm/odm_dlkm/etc"),
-				Name:   proptools.StringPtr("odm_dlkm/etc"),
-			},
-			filesystem.SymlinkDefinition{
-				Target: proptools.StringPtr("/vendor/vendor_dlkm/etc"),
-				Name:   proptools.StringPtr("vendor_dlkm/etc"),
-			},
-		}
+		fsProps.Symlinks = commonSymlinksFromRoot
+		fsProps.Symlinks = append(fsProps.Symlinks,
+			[]filesystem.SymlinkDefinition{
+				{
+					Target: proptools.StringPtr("/data/cache"),
+					Name:   proptools.StringPtr("cache"),
+				},
+				{
+					Target: proptools.StringPtr("/storage/self/primary"),
+					Name:   proptools.StringPtr("sdcard"),
+				},
+				{
+					Target: proptools.StringPtr("/system_dlkm/lib/modules"),
+					Name:   proptools.StringPtr("system/lib/modules"),
+				},
+				{
+					Target: proptools.StringPtr("/product"),
+					Name:   proptools.StringPtr("system/product"),
+				},
+				{
+					Target: proptools.StringPtr("/system_ext"),
+					Name:   proptools.StringPtr("system/system_ext"),
+				},
+				{
+					Target: proptools.StringPtr("/vendor"),
+					Name:   proptools.StringPtr("system/vendor"),
+				},
+			}...,
+		)
 		fsProps.Base_dir = proptools.StringPtr("system")
-		fsProps.Dirs = proptools.NewSimpleConfigurable([]string{
-			// From generic_rootdirs in build/make/target/product/generic/Android.bp
-			"acct",
-			"apex",
-			"bootstrap-apex",
-			"config",
-			"data",
-			"data_mirror",
-			"debug_ramdisk",
-			"dev",
-			"linkerconfig",
-			"metadata",
-			"mnt",
-			"odm",
-			"odm_dlkm",
-			"oem",
-			"postinstall",
-			"proc",
-			"second_stage_resources",
-			"storage",
-			"sys",
-			"system",
-			"system_dlkm",
-			"tmp",
-			"vendor",
-			"vendor_dlkm",
-
-			// from android_rootdirs in build/make/target/product/generic/Android.bp
-			"system_ext",
-			"product",
-		})
+		fsProps.Dirs = proptools.NewSimpleConfigurable(commonPartitionDirs)
 	case "system_ext":
 		if partitionVars.ProductFsverityGenerateMetadata {
 			fsProps.Fsverity.Inputs = []string{
@@ -438,22 +329,35 @@
 			})
 		}
 	case "recovery":
-		// Following https://cs.android.com/android/platform/superproject/main/+/main:build/make/core/Makefile;l=2826;drc=ad7cfb56010cb22c3aa0e70cf71c804352553526
-		fsProps.Dirs = android.NewSimpleConfigurable([]string{
+		dirs := append(commonPartitionDirs, []string{
+			"odm_file_contexts",
+			"odm_property_contexts",
+			"plat_file_contexts",
+			"plat_property_contexts",
+			"plat_service_contexts",
+			"product_file_contexts",
+			"product_property_contexts",
+			"product_service_contexts",
 			"sdcard",
-			"tmp",
-		})
-		fsProps.Symlinks = []filesystem.SymlinkDefinition{
-			{
-				Target: proptools.StringPtr("/system/bin/init"),
-				Name:   proptools.StringPtr("init"),
-			},
-			{
-				Target: proptools.StringPtr("prop.default"),
-				Name:   proptools.StringPtr("default.prop"),
-			},
+			"sepolicy",
+			"system_ext_file_contexts",
+			"system_ext_property_contexts",
+			"system_ext_service_contexts",
+			"vendor_file_contexts",
+			"vendor_property_contexts",
+			"vendor_service_contexts",
+		}...)
+
+		dirsWithRoot := make([]string, len(dirs))
+		for i, dir := range dirs {
+			dirsWithRoot[i] = filepath.Join("root", dir)
 		}
-		fsProps.Base_dir = proptools.StringPtr("recovery")
+
+		fsProps.Dirs = proptools.NewSimpleConfigurable(dirsWithRoot)
+		fsProps.Symlinks = symlinksWithNamePrefix(append(commonSymlinksFromRoot, filesystem.SymlinkDefinition{
+			Target: proptools.StringPtr("prop.default"),
+			Name:   proptools.StringPtr("default.prop"),
+		}), "root")
 	}
 }
 
diff --git a/fsgen/util.go b/fsgen/util.go
index 9ab3ad8..3894c46 100644
--- a/fsgen/util.go
+++ b/fsgen/util.go
@@ -16,6 +16,7 @@
 
 import (
 	"android/soong/android"
+	"android/soong/filesystem"
 	"fmt"
 	"strconv"
 	"strings"
@@ -58,3 +59,12 @@
 
 	return recoveryDensity
 }
+
+// Returns a new list of symlinks with prefix added to the dest directory for all symlinks
+func symlinksWithNamePrefix(symlinks []filesystem.SymlinkDefinition, prefix string) []filesystem.SymlinkDefinition {
+	ret := make([]filesystem.SymlinkDefinition, len(symlinks))
+	for i, symlink := range symlinks {
+		ret[i] = symlink.CopyWithNamePrefix(prefix)
+	}
+	return ret
+}