Sandbox soong_build by changing to root directory
Store the current working directory and then change to the root
directory so that all file accesses must go through helpers in
the android package that properly track dependencies.
Fixes: 146437378
Test: m checkbuild
Change-Id: I12a0f907753fefd1997ab8b4ea2ac331234093cf
diff --git a/android/androidmk.go b/android/androidmk.go
index f3c15e4..dbf3aa8 100644
--- a/android/androidmk.go
+++ b/android/androidmk.go
@@ -323,7 +323,7 @@
return
}
- err := translateAndroidMk(ctx, transMk.String(), androidMkModulesList)
+ err := translateAndroidMk(ctx, absolutePath(transMk.String()), androidMkModulesList)
if err != nil {
ctx.Errorf(err.Error())
}
@@ -364,8 +364,8 @@
}
// Don't write to the file if it hasn't changed
- if _, err := os.Stat(mkFile); !os.IsNotExist(err) {
- if data, err := ioutil.ReadFile(mkFile); err == nil {
+ if _, err := os.Stat(absolutePath(mkFile)); !os.IsNotExist(err) {
+ if data, err := ioutil.ReadFile(absolutePath(mkFile)); err == nil {
matches := buf.Len() == len(data)
if matches {
@@ -383,7 +383,7 @@
}
}
- return ioutil.WriteFile(mkFile, buf.Bytes(), 0666)
+ return ioutil.WriteFile(absolutePath(mkFile), buf.Bytes(), 0666)
}
func translateAndroidMkModule(ctx SingletonContext, w io.Writer, mod blueprint.Module) error {
diff --git a/android/config.go b/android/config.go
index 101f457..3c49c1a 100644
--- a/android/config.go
+++ b/android/config.go
@@ -135,12 +135,12 @@
}
func loadConfig(config *config) error {
- err := loadFromConfigFile(&config.FileConfigurableOptions, config.ConfigFileName)
+ err := loadFromConfigFile(&config.FileConfigurableOptions, absolutePath(config.ConfigFileName))
if err != nil {
return err
}
- return loadFromConfigFile(&config.productVariables, config.ProductVariablesFileName)
+ return loadFromConfigFile(&config.productVariables, absolutePath(config.ProductVariablesFileName))
}
// loads configuration options from a JSON file in the cwd.
@@ -204,6 +204,17 @@
return nil
}
+// NullConfig returns a mostly empty Config for use by standalone tools like dexpreopt_gen that
+// use the android package.
+func NullConfig(buildDir string) Config {
+ return Config{
+ config: &config{
+ buildDir: buildDir,
+ fs: pathtools.OsFs,
+ },
+ }
+}
+
// TestConfig returns a Config object suitable for using for tests
func TestConfig(buildDir string, env map[string]string, bp string, fs map[string][]byte) Config {
envCopy := make(map[string]string)
@@ -320,7 +331,7 @@
buildDir: buildDir,
multilibConflicts: make(map[ArchType]bool),
- fs: pathtools.OsFs,
+ fs: pathtools.NewOsFs(absSrcDir),
}
config.deviceConfig = &deviceConfig{
@@ -350,7 +361,7 @@
}
inMakeFile := filepath.Join(buildDir, ".soong.in_make")
- if _, err := os.Stat(inMakeFile); err == nil {
+ if _, err := os.Stat(absolutePath(inMakeFile)); err == nil {
config.inMake = true
}
@@ -398,6 +409,8 @@
return Config{config}, nil
}
+var TestConfigOsFs = map[string][]byte{}
+
// mockFileSystem replaces all reads with accesses to the provided map of
// filenames to contents stored as a byte slice.
func (c *config) mockFileSystem(bp string, fs map[string][]byte) {
@@ -901,8 +914,13 @@
return c.productVariables.BootJars
}
-func (c *config) DexpreoptGlobalConfig() string {
- return String(c.productVariables.DexpreoptGlobalConfig)
+func (c *config) DexpreoptGlobalConfig(ctx PathContext) ([]byte, error) {
+ if c.productVariables.DexpreoptGlobalConfig == nil {
+ return nil, nil
+ }
+ path := absolutePath(*c.productVariables.DexpreoptGlobalConfig)
+ ctx.AddNinjaFileDeps(path)
+ return ioutil.ReadFile(path)
}
func (c *config) FrameworksBaseDirExists(ctx PathContext) bool {
diff --git a/android/env.go b/android/env.go
index d9f2db2..46bd3d6 100644
--- a/android/env.go
+++ b/android/env.go
@@ -52,6 +52,17 @@
os.Clearenv()
}
+// getenv checks either os.Getenv or originalEnv so that it works before or after the init()
+// function above. It doesn't add any dependencies on the environment variable, so it should
+// only be used for values that won't change. For values that might change use ctx.Config().Getenv.
+func getenv(key string) string {
+ if originalEnv == nil {
+ return os.Getenv(key)
+ } else {
+ return originalEnv[key]
+ }
+}
+
func EnvSingleton() Singleton {
return &envSingleton{}
}
@@ -66,7 +77,12 @@
return
}
- err := env.WriteEnvFile(envFile.String(), envDeps)
+ data, err := env.EnvFileContents(envDeps)
+ if err != nil {
+ ctx.Errorf(err.Error())
+ }
+
+ err = WriteFileToOutputDir(envFile, data, 0666)
if err != nil {
ctx.Errorf(err.Error())
}
diff --git a/android/makevars.go b/android/makevars.go
index 38a028c..aba4cce 100644
--- a/android/makevars.go
+++ b/android/makevars.go
@@ -23,7 +23,6 @@
"strings"
"github.com/google/blueprint"
- "github.com/google/blueprint/pathtools"
"github.com/google/blueprint/proptools"
)
@@ -41,7 +40,6 @@
Config() Config
DeviceConfig() DeviceConfig
AddNinjaFileDeps(deps ...string)
- Fs() pathtools.FileSystem
ModuleName(module blueprint.Module) string
ModuleDir(module blueprint.Module) string
@@ -151,7 +149,8 @@
return
}
- outFile := PathForOutput(ctx, "make_vars"+proptools.String(ctx.Config().productVariables.Make_suffix)+".mk").String()
+ outFile := absolutePath(PathForOutput(ctx,
+ "make_vars"+proptools.String(ctx.Config().productVariables.Make_suffix)+".mk").String())
if ctx.Failed() {
return
@@ -175,15 +174,15 @@
outBytes := s.writeVars(vars)
- if _, err := os.Stat(outFile); err == nil {
- if data, err := ioutil.ReadFile(outFile); err == nil {
+ if _, err := os.Stat(absolutePath(outFile)); err == nil {
+ if data, err := ioutil.ReadFile(absolutePath(outFile)); err == nil {
if bytes.Equal(data, outBytes) {
return
}
}
}
- if err := ioutil.WriteFile(outFile, outBytes, 0666); err != nil {
+ if err := ioutil.WriteFile(absolutePath(outFile), outBytes, 0666); err != nil {
ctx.Errorf(err.Error())
}
}
diff --git a/android/module.go b/android/module.go
index c998007..67d1f12 100644
--- a/android/module.go
+++ b/android/module.go
@@ -16,13 +16,13 @@
import (
"fmt"
+ "os"
"path"
"path/filepath"
"strings"
"text/scanner"
"github.com/google/blueprint"
- "github.com/google/blueprint/pathtools"
"github.com/google/blueprint/proptools"
)
@@ -91,7 +91,8 @@
Glob(globPattern string, excludes []string) Paths
GlobFiles(globPattern string, excludes []string) Paths
- Fs() pathtools.FileSystem
+ IsSymlink(path Path) bool
+ Readlink(path Path) string
}
// BaseModuleContext is the same as blueprint.BaseModuleContext except that Config() returns
@@ -1172,6 +1173,22 @@
return pathsForModuleSrcFromFullPath(e, ret, false)
}
+func (b *earlyModuleContext) IsSymlink(path Path) bool {
+ fileInfo, err := b.config.fs.Lstat(path.String())
+ if err != nil {
+ b.ModuleErrorf("os.Lstat(%q) failed: %s", path.String(), err)
+ }
+ return fileInfo.Mode()&os.ModeSymlink == os.ModeSymlink
+}
+
+func (b *earlyModuleContext) Readlink(path Path) string {
+ dest, err := b.config.fs.Readlink(path.String())
+ if err != nil {
+ b.ModuleErrorf("os.Readlink(%q) failed: %s", path.String(), err)
+ }
+ return dest
+}
+
func (e *earlyModuleContext) Module() Module {
module, _ := e.EarlyModuleContext.Module().(Module)
return module
diff --git a/android/package_ctx.go b/android/package_ctx.go
index d3527fa..a228910 100644
--- a/android/package_ctx.go
+++ b/android/package_ctx.go
@@ -19,7 +19,6 @@
"strings"
"github.com/google/blueprint"
- "github.com/google/blueprint/pathtools"
)
// PackageContext is a wrapper for blueprint.PackageContext that adds
@@ -60,10 +59,6 @@
e.pctx.AddNinjaFileDeps(deps...)
}
-func (e *configErrorWrapper) Fs() pathtools.FileSystem {
- return nil
-}
-
type PackageVarContext interface {
PathContext
errorfContext
diff --git a/android/paths.go b/android/paths.go
index a03fe17..02f56d0 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -16,6 +16,8 @@
import (
"fmt"
+ "io/ioutil"
+ "os"
"path/filepath"
"reflect"
"sort"
@@ -25,10 +27,11 @@
"github.com/google/blueprint/pathtools"
)
+var absSrcDir string
+
// PathContext is the subset of a (Module|Singleton)Context required by the
// Path methods.
type PathContext interface {
- Fs() pathtools.FileSystem
Config() Config
AddNinjaFileDeps(deps ...string)
}
@@ -390,7 +393,7 @@
return PathsWithModuleSrcSubDir(ctx, paths, ""), nil
} else {
p := pathForModuleSrc(ctx, s)
- if exists, _, err := ctx.Fs().Exists(p.String()); err != nil {
+ if exists, _, err := ctx.Config().fs.Exists(p.String()); err != nil {
reportPathErrorf(ctx, "%s: %s", p, err.Error())
} else if !exists {
reportPathErrorf(ctx, "module source path %q does not exist", p)
@@ -720,7 +723,7 @@
var deps []string
// We cannot add build statements in this context, so we fall back to
// AddNinjaFileDeps
- files, deps, err = pathtools.Glob(path.String(), nil, pathtools.FollowSymlinks)
+ files, deps, err = ctx.Config().fs.Glob(path.String(), nil, pathtools.FollowSymlinks)
ctx.AddNinjaFileDeps(deps...)
}
@@ -752,7 +755,7 @@
if !exists {
modCtx.AddMissingDependencies([]string{path.String()})
}
- } else if exists, _, err := ctx.Fs().Exists(path.String()); err != nil {
+ } else if exists, _, err := ctx.Config().fs.Exists(path.String()); err != nil {
reportPathErrorf(ctx, "%s: %s", path, err.Error())
} else if !exists {
reportPathErrorf(ctx, "source path %q does not exist", path)
@@ -1356,7 +1359,6 @@
config Config
}
-func (x *testPathContext) Fs() pathtools.FileSystem { return x.config.fs }
func (x *testPathContext) Config() Config { return x.config }
func (x *testPathContext) AddNinjaFileDeps(...string) {}
@@ -1402,3 +1404,16 @@
}
return rel, true, nil
}
+
+// Writes a file to the output directory. Attempting to write directly to the output directory
+// will fail due to the sandbox of the soong_build process.
+func WriteFileToOutputDir(path WritablePath, data []byte, perm os.FileMode) error {
+ return ioutil.WriteFile(absolutePath(path.String()), data, perm)
+}
+
+func absolutePath(path string) string {
+ if filepath.IsAbs(path) {
+ return path
+ }
+ return filepath.Join(absSrcDir, path)
+}
diff --git a/android/paths_test.go b/android/paths_test.go
index ec5e598..46e3e1f 100644
--- a/android/paths_test.go
+++ b/android/paths_test.go
@@ -21,7 +21,6 @@
"strings"
"testing"
- "github.com/google/blueprint/pathtools"
"github.com/google/blueprint/proptools"
)
@@ -207,10 +206,6 @@
inRoot bool
}
-func (moduleInstallPathContextImpl) Fs() pathtools.FileSystem {
- return pathtools.MockFs(nil)
-}
-
func (m moduleInstallPathContextImpl) Config() Config {
return m.baseModuleContext.config
}
diff --git a/android/register.go b/android/register.go
index b5defec..b48d3d1 100644
--- a/android/register.go
+++ b/android/register.go
@@ -84,7 +84,9 @@
}
func NewContext() *Context {
- return &Context{blueprint.NewContext()}
+ ctx := &Context{blueprint.NewContext()}
+ ctx.SetSrcDir(absSrcDir)
+ return ctx
}
func (ctx *Context) Register() {
diff --git a/android/sandbox.go b/android/sandbox.go
new file mode 100644
index 0000000..ed022fb
--- /dev/null
+++ b/android/sandbox.go
@@ -0,0 +1,47 @@
+// Copyright 2020 Google Inc. All rights reserved.
+//
+// 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 android
+
+import (
+ "fmt"
+ "os"
+)
+
+func init() {
+ // Stash the working directory in a private variable and then change the working directory
+ // to "/", which will prevent untracked accesses to files by Go Soong plugins. The
+ // SOONG_SANDBOX_SOONG_BUILD environment variable is set by soong_ui, and is not
+ // overrideable on the command line.
+
+ orig, err := os.Getwd()
+ if err != nil {
+ panic(fmt.Errorf("failed to get working directory: %s", err))
+ }
+ absSrcDir = orig
+
+ if getenv("SOONG_SANDBOX_SOONG_BUILD") == "true" {
+ err = os.Chdir("/")
+ if err != nil {
+ panic(fmt.Errorf("failed to change working directory to '/': %s", err))
+ }
+ }
+}
+
+// DO NOT USE THIS FUNCTION IN NEW CODE.
+// Deprecated: This function will be removed as soon as the existing use cases that use it have been
+// replaced.
+func AbsSrcDirForExistingUseCases() string {
+ return absSrcDir
+}
diff --git a/android/singleton.go b/android/singleton.go
index 5519ca0..91268ad 100644
--- a/android/singleton.go
+++ b/android/singleton.go
@@ -16,7 +16,6 @@
import (
"github.com/google/blueprint"
- "github.com/google/blueprint/pathtools"
)
// SingletonContext
@@ -74,8 +73,6 @@
// builder whenever a file matching the pattern as added or removed, without rerunning if a
// file that does not match the pattern is added to a searched directory.
GlobWithDeps(pattern string, excludes []string) ([]string, error)
-
- Fs() pathtools.FileSystem
}
type singletonAdaptor struct {