xref: /aosp_15_r20/build/soong/cmd/sbox/sbox.go (revision 333d2b3687b3a337dbcca9d65000bca186795e39)
1*333d2b36SAndroid Build Coastguard Worker// Copyright 2017 Google Inc. All rights reserved.
2*333d2b36SAndroid Build Coastguard Worker//
3*333d2b36SAndroid Build Coastguard Worker// Licensed under the Apache License, Version 2.0 (the "License");
4*333d2b36SAndroid Build Coastguard Worker// you may not use this file except in compliance with the License.
5*333d2b36SAndroid Build Coastguard Worker// You may obtain a copy of the License at
6*333d2b36SAndroid Build Coastguard Worker//
7*333d2b36SAndroid Build Coastguard Worker//     http://www.apache.org/licenses/LICENSE-2.0
8*333d2b36SAndroid Build Coastguard Worker//
9*333d2b36SAndroid Build Coastguard Worker// Unless required by applicable law or agreed to in writing, software
10*333d2b36SAndroid Build Coastguard Worker// distributed under the License is distributed on an "AS IS" BASIS,
11*333d2b36SAndroid Build Coastguard Worker// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*333d2b36SAndroid Build Coastguard Worker// See the License for the specific language governing permissions and
13*333d2b36SAndroid Build Coastguard Worker// limitations under the License.
14*333d2b36SAndroid Build Coastguard Worker
15*333d2b36SAndroid Build Coastguard Workerpackage main
16*333d2b36SAndroid Build Coastguard Worker
17*333d2b36SAndroid Build Coastguard Workerimport (
18*333d2b36SAndroid Build Coastguard Worker	"bytes"
19*333d2b36SAndroid Build Coastguard Worker	"crypto/sha1"
20*333d2b36SAndroid Build Coastguard Worker	"encoding/hex"
21*333d2b36SAndroid Build Coastguard Worker	"errors"
22*333d2b36SAndroid Build Coastguard Worker	"flag"
23*333d2b36SAndroid Build Coastguard Worker	"fmt"
24*333d2b36SAndroid Build Coastguard Worker	"io"
25*333d2b36SAndroid Build Coastguard Worker	"io/fs"
26*333d2b36SAndroid Build Coastguard Worker	"io/ioutil"
27*333d2b36SAndroid Build Coastguard Worker	"os"
28*333d2b36SAndroid Build Coastguard Worker	"os/exec"
29*333d2b36SAndroid Build Coastguard Worker	"path/filepath"
30*333d2b36SAndroid Build Coastguard Worker	"regexp"
31*333d2b36SAndroid Build Coastguard Worker	"strconv"
32*333d2b36SAndroid Build Coastguard Worker	"strings"
33*333d2b36SAndroid Build Coastguard Worker	"time"
34*333d2b36SAndroid Build Coastguard Worker
35*333d2b36SAndroid Build Coastguard Worker	"android/soong/cmd/sbox/sbox_proto"
36*333d2b36SAndroid Build Coastguard Worker	"android/soong/makedeps"
37*333d2b36SAndroid Build Coastguard Worker	"android/soong/response"
38*333d2b36SAndroid Build Coastguard Worker
39*333d2b36SAndroid Build Coastguard Worker	"google.golang.org/protobuf/encoding/prototext"
40*333d2b36SAndroid Build Coastguard Worker)
41*333d2b36SAndroid Build Coastguard Worker
42*333d2b36SAndroid Build Coastguard Workervar (
43*333d2b36SAndroid Build Coastguard Worker	sandboxesRoot  string
44*333d2b36SAndroid Build Coastguard Worker	outputDir      string
45*333d2b36SAndroid Build Coastguard Worker	manifestFile   string
46*333d2b36SAndroid Build Coastguard Worker	keepOutDir     bool
47*333d2b36SAndroid Build Coastguard Worker	writeIfChanged bool
48*333d2b36SAndroid Build Coastguard Worker)
49*333d2b36SAndroid Build Coastguard Worker
50*333d2b36SAndroid Build Coastguard Workerconst (
51*333d2b36SAndroid Build Coastguard Worker	depFilePlaceholder    = "__SBOX_DEPFILE__"
52*333d2b36SAndroid Build Coastguard Worker	sandboxDirPlaceholder = "__SBOX_SANDBOX_DIR__"
53*333d2b36SAndroid Build Coastguard Worker)
54*333d2b36SAndroid Build Coastguard Worker
55*333d2b36SAndroid Build Coastguard Workervar envVarNameRegex = regexp.MustCompile("^[a-zA-Z0-9_-]+$")
56*333d2b36SAndroid Build Coastguard Worker
57*333d2b36SAndroid Build Coastguard Workerfunc init() {
58*333d2b36SAndroid Build Coastguard Worker	flag.StringVar(&sandboxesRoot, "sandbox-path", "",
59*333d2b36SAndroid Build Coastguard Worker		"root of temp directory to put the sandbox into")
60*333d2b36SAndroid Build Coastguard Worker	flag.StringVar(&outputDir, "output-dir", "",
61*333d2b36SAndroid Build Coastguard Worker		"directory which will contain all output files and only output files")
62*333d2b36SAndroid Build Coastguard Worker	flag.StringVar(&manifestFile, "manifest", "",
63*333d2b36SAndroid Build Coastguard Worker		"textproto manifest describing the sandboxed command(s)")
64*333d2b36SAndroid Build Coastguard Worker	flag.BoolVar(&keepOutDir, "keep-out-dir", false,
65*333d2b36SAndroid Build Coastguard Worker		"whether to keep the sandbox directory when done")
66*333d2b36SAndroid Build Coastguard Worker	flag.BoolVar(&writeIfChanged, "write-if-changed", false,
67*333d2b36SAndroid Build Coastguard Worker		"only write the output files if they have changed")
68*333d2b36SAndroid Build Coastguard Worker}
69*333d2b36SAndroid Build Coastguard Worker
70*333d2b36SAndroid Build Coastguard Workerfunc usageViolation(violation string) {
71*333d2b36SAndroid Build Coastguard Worker	if violation != "" {
72*333d2b36SAndroid Build Coastguard Worker		fmt.Fprintf(os.Stderr, "Usage error: %s.\n\n", violation)
73*333d2b36SAndroid Build Coastguard Worker	}
74*333d2b36SAndroid Build Coastguard Worker
75*333d2b36SAndroid Build Coastguard Worker	fmt.Fprintf(os.Stderr,
76*333d2b36SAndroid Build Coastguard Worker		"Usage: sbox --manifest <manifest> --sandbox-path <sandboxPath>\n")
77*333d2b36SAndroid Build Coastguard Worker
78*333d2b36SAndroid Build Coastguard Worker	flag.PrintDefaults()
79*333d2b36SAndroid Build Coastguard Worker
80*333d2b36SAndroid Build Coastguard Worker	os.Exit(1)
81*333d2b36SAndroid Build Coastguard Worker}
82*333d2b36SAndroid Build Coastguard Worker
83*333d2b36SAndroid Build Coastguard Workerfunc main() {
84*333d2b36SAndroid Build Coastguard Worker	flag.Usage = func() {
85*333d2b36SAndroid Build Coastguard Worker		usageViolation("")
86*333d2b36SAndroid Build Coastguard Worker	}
87*333d2b36SAndroid Build Coastguard Worker	flag.Parse()
88*333d2b36SAndroid Build Coastguard Worker
89*333d2b36SAndroid Build Coastguard Worker	error := run()
90*333d2b36SAndroid Build Coastguard Worker	if error != nil {
91*333d2b36SAndroid Build Coastguard Worker		fmt.Fprintln(os.Stderr, error)
92*333d2b36SAndroid Build Coastguard Worker		os.Exit(1)
93*333d2b36SAndroid Build Coastguard Worker	}
94*333d2b36SAndroid Build Coastguard Worker}
95*333d2b36SAndroid Build Coastguard Worker
96*333d2b36SAndroid Build Coastguard Workerfunc findAllFilesUnder(root string) (paths []string) {
97*333d2b36SAndroid Build Coastguard Worker	paths = []string{}
98*333d2b36SAndroid Build Coastguard Worker	filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
99*333d2b36SAndroid Build Coastguard Worker		if !info.IsDir() {
100*333d2b36SAndroid Build Coastguard Worker			relPath, err := filepath.Rel(root, path)
101*333d2b36SAndroid Build Coastguard Worker			if err != nil {
102*333d2b36SAndroid Build Coastguard Worker				// couldn't find relative path from ancestor?
103*333d2b36SAndroid Build Coastguard Worker				panic(err)
104*333d2b36SAndroid Build Coastguard Worker			}
105*333d2b36SAndroid Build Coastguard Worker			paths = append(paths, relPath)
106*333d2b36SAndroid Build Coastguard Worker		}
107*333d2b36SAndroid Build Coastguard Worker		return nil
108*333d2b36SAndroid Build Coastguard Worker	})
109*333d2b36SAndroid Build Coastguard Worker	return paths
110*333d2b36SAndroid Build Coastguard Worker}
111*333d2b36SAndroid Build Coastguard Worker
112*333d2b36SAndroid Build Coastguard Workerfunc run() error {
113*333d2b36SAndroid Build Coastguard Worker	if manifestFile == "" {
114*333d2b36SAndroid Build Coastguard Worker		usageViolation("--manifest <manifest> is required and must be non-empty")
115*333d2b36SAndroid Build Coastguard Worker	}
116*333d2b36SAndroid Build Coastguard Worker	if sandboxesRoot == "" {
117*333d2b36SAndroid Build Coastguard Worker		// In practice, the value of sandboxesRoot will mostly likely be at a fixed location relative to OUT_DIR,
118*333d2b36SAndroid Build Coastguard Worker		// and the sbox executable will most likely be at a fixed location relative to OUT_DIR too, so
119*333d2b36SAndroid Build Coastguard Worker		// the value of sandboxesRoot will most likely be at a fixed location relative to the sbox executable
120*333d2b36SAndroid Build Coastguard Worker		// However, Soong also needs to be able to separately remove the sandbox directory on startup (if it has anything left in it)
121*333d2b36SAndroid Build Coastguard Worker		// and by passing it as a parameter we don't need to duplicate its value
122*333d2b36SAndroid Build Coastguard Worker		usageViolation("--sandbox-path <sandboxPath> is required and must be non-empty")
123*333d2b36SAndroid Build Coastguard Worker	}
124*333d2b36SAndroid Build Coastguard Worker
125*333d2b36SAndroid Build Coastguard Worker	manifest, err := readManifest(manifestFile)
126*333d2b36SAndroid Build Coastguard Worker	if err != nil {
127*333d2b36SAndroid Build Coastguard Worker		return err
128*333d2b36SAndroid Build Coastguard Worker	}
129*333d2b36SAndroid Build Coastguard Worker
130*333d2b36SAndroid Build Coastguard Worker	if len(manifest.Commands) == 0 {
131*333d2b36SAndroid Build Coastguard Worker		return fmt.Errorf("at least one commands entry is required in %q", manifestFile)
132*333d2b36SAndroid Build Coastguard Worker	}
133*333d2b36SAndroid Build Coastguard Worker
134*333d2b36SAndroid Build Coastguard Worker	// setup sandbox directory
135*333d2b36SAndroid Build Coastguard Worker	err = os.MkdirAll(sandboxesRoot, 0777)
136*333d2b36SAndroid Build Coastguard Worker	if err != nil {
137*333d2b36SAndroid Build Coastguard Worker		return fmt.Errorf("failed to create %q: %w", sandboxesRoot, err)
138*333d2b36SAndroid Build Coastguard Worker	}
139*333d2b36SAndroid Build Coastguard Worker
140*333d2b36SAndroid Build Coastguard Worker	// This tool assumes that there are no two concurrent runs with the same
141*333d2b36SAndroid Build Coastguard Worker	// manifestFile. It should therefore be safe to use the hash of the
142*333d2b36SAndroid Build Coastguard Worker	// manifestFile as the temporary directory name. We do this because it
143*333d2b36SAndroid Build Coastguard Worker	// makes the temporary directory name deterministic. There are some
144*333d2b36SAndroid Build Coastguard Worker	// tools that embed the name of the temporary output in the output, and
145*333d2b36SAndroid Build Coastguard Worker	// they otherwise cause non-determinism, which then poisons actions
146*333d2b36SAndroid Build Coastguard Worker	// depending on this one.
147*333d2b36SAndroid Build Coastguard Worker	hash := sha1.New()
148*333d2b36SAndroid Build Coastguard Worker	hash.Write([]byte(manifestFile))
149*333d2b36SAndroid Build Coastguard Worker	tempDir := filepath.Join(sandboxesRoot, "sbox", hex.EncodeToString(hash.Sum(nil)))
150*333d2b36SAndroid Build Coastguard Worker
151*333d2b36SAndroid Build Coastguard Worker	err = os.RemoveAll(tempDir)
152*333d2b36SAndroid Build Coastguard Worker	if err != nil {
153*333d2b36SAndroid Build Coastguard Worker		return err
154*333d2b36SAndroid Build Coastguard Worker	}
155*333d2b36SAndroid Build Coastguard Worker	err = os.MkdirAll(tempDir, 0777)
156*333d2b36SAndroid Build Coastguard Worker	if err != nil {
157*333d2b36SAndroid Build Coastguard Worker		return fmt.Errorf("failed to create temporary dir in %q: %w", sandboxesRoot, err)
158*333d2b36SAndroid Build Coastguard Worker	}
159*333d2b36SAndroid Build Coastguard Worker
160*333d2b36SAndroid Build Coastguard Worker	// In the common case, the following line of code is what removes the sandbox
161*333d2b36SAndroid Build Coastguard Worker	// If a fatal error occurs (such as if our Go process is killed unexpectedly),
162*333d2b36SAndroid Build Coastguard Worker	// then at the beginning of the next build, Soong will wipe the temporary
163*333d2b36SAndroid Build Coastguard Worker	// directory.
164*333d2b36SAndroid Build Coastguard Worker	defer func() {
165*333d2b36SAndroid Build Coastguard Worker		// in some cases we decline to remove the temp dir, to facilitate debugging
166*333d2b36SAndroid Build Coastguard Worker		if !keepOutDir {
167*333d2b36SAndroid Build Coastguard Worker			os.RemoveAll(tempDir)
168*333d2b36SAndroid Build Coastguard Worker		}
169*333d2b36SAndroid Build Coastguard Worker	}()
170*333d2b36SAndroid Build Coastguard Worker
171*333d2b36SAndroid Build Coastguard Worker	// If there is more than one command in the manifest use a separate directory for each one.
172*333d2b36SAndroid Build Coastguard Worker	useSubDir := len(manifest.Commands) > 1
173*333d2b36SAndroid Build Coastguard Worker	var commandDepFiles []string
174*333d2b36SAndroid Build Coastguard Worker
175*333d2b36SAndroid Build Coastguard Worker	for i, command := range manifest.Commands {
176*333d2b36SAndroid Build Coastguard Worker		localTempDir := tempDir
177*333d2b36SAndroid Build Coastguard Worker		if useSubDir {
178*333d2b36SAndroid Build Coastguard Worker			localTempDir = filepath.Join(localTempDir, strconv.Itoa(i))
179*333d2b36SAndroid Build Coastguard Worker		}
180*333d2b36SAndroid Build Coastguard Worker		depFile, err := runCommand(command, localTempDir, i)
181*333d2b36SAndroid Build Coastguard Worker		if err != nil {
182*333d2b36SAndroid Build Coastguard Worker			// Running the command failed, keep the temporary output directory around in
183*333d2b36SAndroid Build Coastguard Worker			// case a user wants to inspect it for debugging purposes.  Soong will delete
184*333d2b36SAndroid Build Coastguard Worker			// it at the beginning of the next build anyway.
185*333d2b36SAndroid Build Coastguard Worker			keepOutDir = true
186*333d2b36SAndroid Build Coastguard Worker			return err
187*333d2b36SAndroid Build Coastguard Worker		}
188*333d2b36SAndroid Build Coastguard Worker		if depFile != "" {
189*333d2b36SAndroid Build Coastguard Worker			commandDepFiles = append(commandDepFiles, depFile)
190*333d2b36SAndroid Build Coastguard Worker		}
191*333d2b36SAndroid Build Coastguard Worker	}
192*333d2b36SAndroid Build Coastguard Worker
193*333d2b36SAndroid Build Coastguard Worker	outputDepFile := manifest.GetOutputDepfile()
194*333d2b36SAndroid Build Coastguard Worker	if len(commandDepFiles) > 0 && outputDepFile == "" {
195*333d2b36SAndroid Build Coastguard Worker		return fmt.Errorf("Sandboxed commands used %s but output depfile is not set in manifest file",
196*333d2b36SAndroid Build Coastguard Worker			depFilePlaceholder)
197*333d2b36SAndroid Build Coastguard Worker	}
198*333d2b36SAndroid Build Coastguard Worker
199*333d2b36SAndroid Build Coastguard Worker	if outputDepFile != "" {
200*333d2b36SAndroid Build Coastguard Worker		// Merge the depfiles from each command in the manifest to a single output depfile.
201*333d2b36SAndroid Build Coastguard Worker		err = rewriteDepFiles(commandDepFiles, outputDepFile)
202*333d2b36SAndroid Build Coastguard Worker		if err != nil {
203*333d2b36SAndroid Build Coastguard Worker			return fmt.Errorf("failed merging depfiles: %w", err)
204*333d2b36SAndroid Build Coastguard Worker		}
205*333d2b36SAndroid Build Coastguard Worker	}
206*333d2b36SAndroid Build Coastguard Worker
207*333d2b36SAndroid Build Coastguard Worker	return nil
208*333d2b36SAndroid Build Coastguard Worker}
209*333d2b36SAndroid Build Coastguard Worker
210*333d2b36SAndroid Build Coastguard Worker// createCommandScript will create and return an exec.Cmd that runs rawCommand.
211*333d2b36SAndroid Build Coastguard Worker//
212*333d2b36SAndroid Build Coastguard Worker// rawCommand is executed via a script in the sandbox.
213*333d2b36SAndroid Build Coastguard Worker// scriptPath is the temporary where the script is created.
214*333d2b36SAndroid Build Coastguard Worker// scriptPathInSandbox is the path to the script in the sbox environment.
215*333d2b36SAndroid Build Coastguard Worker//
216*333d2b36SAndroid Build Coastguard Worker// returns an exec.Cmd that can be ran from within sbox context if no error, or nil if error.
217*333d2b36SAndroid Build Coastguard Worker// caller must ensure script is cleaned up if function succeeds.
218*333d2b36SAndroid Build Coastguard Workerfunc createCommandScript(rawCommand, scriptPath, scriptPathInSandbox string) (*exec.Cmd, error) {
219*333d2b36SAndroid Build Coastguard Worker	err := os.WriteFile(scriptPath, []byte(rawCommand), 0644)
220*333d2b36SAndroid Build Coastguard Worker	if err != nil {
221*333d2b36SAndroid Build Coastguard Worker		return nil, fmt.Errorf("failed to write command %s... to %s",
222*333d2b36SAndroid Build Coastguard Worker			rawCommand[0:40], scriptPath)
223*333d2b36SAndroid Build Coastguard Worker	}
224*333d2b36SAndroid Build Coastguard Worker	return exec.Command("bash", scriptPathInSandbox), nil
225*333d2b36SAndroid Build Coastguard Worker}
226*333d2b36SAndroid Build Coastguard Worker
227*333d2b36SAndroid Build Coastguard Worker// readManifest reads an sbox manifest from a textproto file.
228*333d2b36SAndroid Build Coastguard Workerfunc readManifest(file string) (*sbox_proto.Manifest, error) {
229*333d2b36SAndroid Build Coastguard Worker	manifestData, err := ioutil.ReadFile(file)
230*333d2b36SAndroid Build Coastguard Worker	if err != nil {
231*333d2b36SAndroid Build Coastguard Worker		return nil, fmt.Errorf("error reading manifest %q: %w", file, err)
232*333d2b36SAndroid Build Coastguard Worker	}
233*333d2b36SAndroid Build Coastguard Worker
234*333d2b36SAndroid Build Coastguard Worker	manifest := sbox_proto.Manifest{}
235*333d2b36SAndroid Build Coastguard Worker
236*333d2b36SAndroid Build Coastguard Worker	err = prototext.Unmarshal(manifestData, &manifest)
237*333d2b36SAndroid Build Coastguard Worker	if err != nil {
238*333d2b36SAndroid Build Coastguard Worker		return nil, fmt.Errorf("error parsing manifest %q: %w", file, err)
239*333d2b36SAndroid Build Coastguard Worker	}
240*333d2b36SAndroid Build Coastguard Worker
241*333d2b36SAndroid Build Coastguard Worker	return &manifest, nil
242*333d2b36SAndroid Build Coastguard Worker}
243*333d2b36SAndroid Build Coastguard Worker
244*333d2b36SAndroid Build Coastguard Workerfunc createEnv(command *sbox_proto.Command) ([]string, error) {
245*333d2b36SAndroid Build Coastguard Worker	env := []string{}
246*333d2b36SAndroid Build Coastguard Worker	if command.DontInheritEnv == nil || !*command.DontInheritEnv {
247*333d2b36SAndroid Build Coastguard Worker		env = os.Environ()
248*333d2b36SAndroid Build Coastguard Worker	}
249*333d2b36SAndroid Build Coastguard Worker	for _, envVar := range command.Env {
250*333d2b36SAndroid Build Coastguard Worker		if envVar.Name == nil || !envVarNameRegex.MatchString(*envVar.Name) {
251*333d2b36SAndroid Build Coastguard Worker			name := "nil"
252*333d2b36SAndroid Build Coastguard Worker			if envVar.Name != nil {
253*333d2b36SAndroid Build Coastguard Worker				name = *envVar.Name
254*333d2b36SAndroid Build Coastguard Worker			}
255*333d2b36SAndroid Build Coastguard Worker			return nil, fmt.Errorf("Invalid environment variable name: %q", name)
256*333d2b36SAndroid Build Coastguard Worker		}
257*333d2b36SAndroid Build Coastguard Worker		if envVar.State == nil {
258*333d2b36SAndroid Build Coastguard Worker			return nil, fmt.Errorf("Must set state")
259*333d2b36SAndroid Build Coastguard Worker		}
260*333d2b36SAndroid Build Coastguard Worker		switch state := envVar.State.(type) {
261*333d2b36SAndroid Build Coastguard Worker		case *sbox_proto.EnvironmentVariable_Value:
262*333d2b36SAndroid Build Coastguard Worker			env = append(env, *envVar.Name+"="+state.Value)
263*333d2b36SAndroid Build Coastguard Worker		case *sbox_proto.EnvironmentVariable_Unset:
264*333d2b36SAndroid Build Coastguard Worker			if !state.Unset {
265*333d2b36SAndroid Build Coastguard Worker				return nil, fmt.Errorf("Can't have unset set to false")
266*333d2b36SAndroid Build Coastguard Worker			}
267*333d2b36SAndroid Build Coastguard Worker			prefix := *envVar.Name + "="
268*333d2b36SAndroid Build Coastguard Worker			for i := 0; i < len(env); i++ {
269*333d2b36SAndroid Build Coastguard Worker				if strings.HasPrefix(env[i], prefix) {
270*333d2b36SAndroid Build Coastguard Worker					env = append(env[:i], env[i+1:]...)
271*333d2b36SAndroid Build Coastguard Worker					i--
272*333d2b36SAndroid Build Coastguard Worker				}
273*333d2b36SAndroid Build Coastguard Worker			}
274*333d2b36SAndroid Build Coastguard Worker		case *sbox_proto.EnvironmentVariable_Inherit:
275*333d2b36SAndroid Build Coastguard Worker			if !state.Inherit {
276*333d2b36SAndroid Build Coastguard Worker				return nil, fmt.Errorf("Can't have inherit set to false")
277*333d2b36SAndroid Build Coastguard Worker			}
278*333d2b36SAndroid Build Coastguard Worker			val, ok := os.LookupEnv(*envVar.Name)
279*333d2b36SAndroid Build Coastguard Worker			if ok {
280*333d2b36SAndroid Build Coastguard Worker				env = append(env, *envVar.Name+"="+val)
281*333d2b36SAndroid Build Coastguard Worker			}
282*333d2b36SAndroid Build Coastguard Worker		default:
283*333d2b36SAndroid Build Coastguard Worker			return nil, fmt.Errorf("Unhandled state type")
284*333d2b36SAndroid Build Coastguard Worker		}
285*333d2b36SAndroid Build Coastguard Worker	}
286*333d2b36SAndroid Build Coastguard Worker	return env, nil
287*333d2b36SAndroid Build Coastguard Worker}
288*333d2b36SAndroid Build Coastguard Worker
289*333d2b36SAndroid Build Coastguard Worker// runCommand runs a single command from a manifest.  If the command references the
290*333d2b36SAndroid Build Coastguard Worker// __SBOX_DEPFILE__ placeholder it returns the name of the depfile that was used.
291*333d2b36SAndroid Build Coastguard Workerfunc runCommand(command *sbox_proto.Command, tempDir string, commandIndex int) (depFile string, err error) {
292*333d2b36SAndroid Build Coastguard Worker	rawCommand := command.GetCommand()
293*333d2b36SAndroid Build Coastguard Worker	if rawCommand == "" {
294*333d2b36SAndroid Build Coastguard Worker		return "", fmt.Errorf("command is required")
295*333d2b36SAndroid Build Coastguard Worker	}
296*333d2b36SAndroid Build Coastguard Worker
297*333d2b36SAndroid Build Coastguard Worker	// Remove files from the output directory
298*333d2b36SAndroid Build Coastguard Worker	err = clearOutputDirectory(command.CopyAfter, outputDir, writeType(writeIfChanged))
299*333d2b36SAndroid Build Coastguard Worker	if err != nil {
300*333d2b36SAndroid Build Coastguard Worker		return "", err
301*333d2b36SAndroid Build Coastguard Worker	}
302*333d2b36SAndroid Build Coastguard Worker
303*333d2b36SAndroid Build Coastguard Worker	pathToTempDirInSbox := tempDir
304*333d2b36SAndroid Build Coastguard Worker	if command.GetChdir() {
305*333d2b36SAndroid Build Coastguard Worker		pathToTempDirInSbox = "."
306*333d2b36SAndroid Build Coastguard Worker	}
307*333d2b36SAndroid Build Coastguard Worker
308*333d2b36SAndroid Build Coastguard Worker	err = os.MkdirAll(tempDir, 0777)
309*333d2b36SAndroid Build Coastguard Worker	if err != nil {
310*333d2b36SAndroid Build Coastguard Worker		return "", fmt.Errorf("failed to create %q: %w", tempDir, err)
311*333d2b36SAndroid Build Coastguard Worker	}
312*333d2b36SAndroid Build Coastguard Worker
313*333d2b36SAndroid Build Coastguard Worker	// Copy in any files specified by the manifest.
314*333d2b36SAndroid Build Coastguard Worker	err = copyFiles(command.CopyBefore, "", tempDir, requireFromExists, alwaysWrite)
315*333d2b36SAndroid Build Coastguard Worker	if err != nil {
316*333d2b36SAndroid Build Coastguard Worker		return "", err
317*333d2b36SAndroid Build Coastguard Worker	}
318*333d2b36SAndroid Build Coastguard Worker	err = copyRspFiles(command.RspFiles, tempDir, pathToTempDirInSbox)
319*333d2b36SAndroid Build Coastguard Worker	if err != nil {
320*333d2b36SAndroid Build Coastguard Worker		return "", err
321*333d2b36SAndroid Build Coastguard Worker	}
322*333d2b36SAndroid Build Coastguard Worker
323*333d2b36SAndroid Build Coastguard Worker	if strings.Contains(rawCommand, depFilePlaceholder) {
324*333d2b36SAndroid Build Coastguard Worker		depFile = filepath.Join(pathToTempDirInSbox, "deps.d")
325*333d2b36SAndroid Build Coastguard Worker		rawCommand = strings.Replace(rawCommand, depFilePlaceholder, depFile, -1)
326*333d2b36SAndroid Build Coastguard Worker	}
327*333d2b36SAndroid Build Coastguard Worker
328*333d2b36SAndroid Build Coastguard Worker	if strings.Contains(rawCommand, sandboxDirPlaceholder) {
329*333d2b36SAndroid Build Coastguard Worker		rawCommand = strings.Replace(rawCommand, sandboxDirPlaceholder, pathToTempDirInSbox, -1)
330*333d2b36SAndroid Build Coastguard Worker	}
331*333d2b36SAndroid Build Coastguard Worker
332*333d2b36SAndroid Build Coastguard Worker	// Emulate ninja's behavior of creating the directories for any output files before
333*333d2b36SAndroid Build Coastguard Worker	// running the command.
334*333d2b36SAndroid Build Coastguard Worker	err = makeOutputDirs(command.CopyAfter, tempDir)
335*333d2b36SAndroid Build Coastguard Worker	if err != nil {
336*333d2b36SAndroid Build Coastguard Worker		return "", err
337*333d2b36SAndroid Build Coastguard Worker	}
338*333d2b36SAndroid Build Coastguard Worker
339*333d2b36SAndroid Build Coastguard Worker	scriptName := fmt.Sprintf("sbox_command.%d.bash", commandIndex)
340*333d2b36SAndroid Build Coastguard Worker	scriptPath := joinPath(tempDir, scriptName)
341*333d2b36SAndroid Build Coastguard Worker	scriptPathInSandbox := joinPath(pathToTempDirInSbox, scriptName)
342*333d2b36SAndroid Build Coastguard Worker	cmd, err := createCommandScript(rawCommand, scriptPath, scriptPathInSandbox)
343*333d2b36SAndroid Build Coastguard Worker	if err != nil {
344*333d2b36SAndroid Build Coastguard Worker		return "", err
345*333d2b36SAndroid Build Coastguard Worker	}
346*333d2b36SAndroid Build Coastguard Worker
347*333d2b36SAndroid Build Coastguard Worker	buf := &bytes.Buffer{}
348*333d2b36SAndroid Build Coastguard Worker	cmd.Stdin = os.Stdin
349*333d2b36SAndroid Build Coastguard Worker	cmd.Stdout = buf
350*333d2b36SAndroid Build Coastguard Worker	cmd.Stderr = buf
351*333d2b36SAndroid Build Coastguard Worker
352*333d2b36SAndroid Build Coastguard Worker	if command.GetChdir() {
353*333d2b36SAndroid Build Coastguard Worker		cmd.Dir = tempDir
354*333d2b36SAndroid Build Coastguard Worker		path := os.Getenv("PATH")
355*333d2b36SAndroid Build Coastguard Worker		absPath, err := makeAbsPathEnv(path)
356*333d2b36SAndroid Build Coastguard Worker		if err != nil {
357*333d2b36SAndroid Build Coastguard Worker			return "", err
358*333d2b36SAndroid Build Coastguard Worker		}
359*333d2b36SAndroid Build Coastguard Worker		err = os.Setenv("PATH", absPath)
360*333d2b36SAndroid Build Coastguard Worker		if err != nil {
361*333d2b36SAndroid Build Coastguard Worker			return "", fmt.Errorf("Failed to update PATH: %w", err)
362*333d2b36SAndroid Build Coastguard Worker		}
363*333d2b36SAndroid Build Coastguard Worker	}
364*333d2b36SAndroid Build Coastguard Worker
365*333d2b36SAndroid Build Coastguard Worker	cmd.Env, err = createEnv(command)
366*333d2b36SAndroid Build Coastguard Worker	if err != nil {
367*333d2b36SAndroid Build Coastguard Worker		return "", err
368*333d2b36SAndroid Build Coastguard Worker	}
369*333d2b36SAndroid Build Coastguard Worker
370*333d2b36SAndroid Build Coastguard Worker	err = cmd.Run()
371*333d2b36SAndroid Build Coastguard Worker
372*333d2b36SAndroid Build Coastguard Worker	if err != nil {
373*333d2b36SAndroid Build Coastguard Worker		// The command failed, do a best effort copy of output files out of the sandbox.  This is
374*333d2b36SAndroid Build Coastguard Worker		// especially useful for linters with baselines that print an error message on failure
375*333d2b36SAndroid Build Coastguard Worker		// with a command to copy the output lint errors to the new baseline.  Use a copy instead of
376*333d2b36SAndroid Build Coastguard Worker		// a move to leave the sandbox intact for manual inspection
377*333d2b36SAndroid Build Coastguard Worker		copyFiles(command.CopyAfter, tempDir, "", allowFromNotExists, writeType(writeIfChanged))
378*333d2b36SAndroid Build Coastguard Worker	}
379*333d2b36SAndroid Build Coastguard Worker
380*333d2b36SAndroid Build Coastguard Worker	// If the command  was executed but failed with an error, print a debugging message before
381*333d2b36SAndroid Build Coastguard Worker	// the command's output so it doesn't scroll the real error message off the screen.
382*333d2b36SAndroid Build Coastguard Worker	if exit, ok := err.(*exec.ExitError); ok && !exit.Success() {
383*333d2b36SAndroid Build Coastguard Worker		fmt.Fprintf(os.Stderr,
384*333d2b36SAndroid Build Coastguard Worker			"The failing command was run inside an sbox sandbox in temporary directory\n"+
385*333d2b36SAndroid Build Coastguard Worker				"%s\n"+
386*333d2b36SAndroid Build Coastguard Worker				"The failing command line can be found in\n"+
387*333d2b36SAndroid Build Coastguard Worker				"%s\n",
388*333d2b36SAndroid Build Coastguard Worker			tempDir, scriptPath)
389*333d2b36SAndroid Build Coastguard Worker	}
390*333d2b36SAndroid Build Coastguard Worker
391*333d2b36SAndroid Build Coastguard Worker	// Write the command's combined stdout/stderr.
392*333d2b36SAndroid Build Coastguard Worker	os.Stdout.Write(buf.Bytes())
393*333d2b36SAndroid Build Coastguard Worker
394*333d2b36SAndroid Build Coastguard Worker	if err != nil {
395*333d2b36SAndroid Build Coastguard Worker		return "", err
396*333d2b36SAndroid Build Coastguard Worker	}
397*333d2b36SAndroid Build Coastguard Worker
398*333d2b36SAndroid Build Coastguard Worker	err = validateOutputFiles(command.CopyAfter, tempDir, outputDir, rawCommand)
399*333d2b36SAndroid Build Coastguard Worker	if err != nil {
400*333d2b36SAndroid Build Coastguard Worker		return "", err
401*333d2b36SAndroid Build Coastguard Worker	}
402*333d2b36SAndroid Build Coastguard Worker
403*333d2b36SAndroid Build Coastguard Worker	// the created files match the declared files; now move them
404*333d2b36SAndroid Build Coastguard Worker	err = moveFiles(command.CopyAfter, tempDir, "", writeType(writeIfChanged))
405*333d2b36SAndroid Build Coastguard Worker	if err != nil {
406*333d2b36SAndroid Build Coastguard Worker		return "", err
407*333d2b36SAndroid Build Coastguard Worker	}
408*333d2b36SAndroid Build Coastguard Worker
409*333d2b36SAndroid Build Coastguard Worker	return depFile, nil
410*333d2b36SAndroid Build Coastguard Worker}
411*333d2b36SAndroid Build Coastguard Worker
412*333d2b36SAndroid Build Coastguard Worker// makeOutputDirs creates directories in the sandbox dir for every file that has a rule to be copied
413*333d2b36SAndroid Build Coastguard Worker// out of the sandbox.  This emulate's Ninja's behavior of creating directories for output files
414*333d2b36SAndroid Build Coastguard Worker// so that the tools don't have to.
415*333d2b36SAndroid Build Coastguard Workerfunc makeOutputDirs(copies []*sbox_proto.Copy, sandboxDir string) error {
416*333d2b36SAndroid Build Coastguard Worker	for _, copyPair := range copies {
417*333d2b36SAndroid Build Coastguard Worker		dir := joinPath(sandboxDir, filepath.Dir(copyPair.GetFrom()))
418*333d2b36SAndroid Build Coastguard Worker		err := os.MkdirAll(dir, 0777)
419*333d2b36SAndroid Build Coastguard Worker		if err != nil {
420*333d2b36SAndroid Build Coastguard Worker			return err
421*333d2b36SAndroid Build Coastguard Worker		}
422*333d2b36SAndroid Build Coastguard Worker	}
423*333d2b36SAndroid Build Coastguard Worker	return nil
424*333d2b36SAndroid Build Coastguard Worker}
425*333d2b36SAndroid Build Coastguard Worker
426*333d2b36SAndroid Build Coastguard Worker// validateOutputFiles verifies that all files that have a rule to be copied out of the sandbox
427*333d2b36SAndroid Build Coastguard Worker// were created by the command.
428*333d2b36SAndroid Build Coastguard Workerfunc validateOutputFiles(copies []*sbox_proto.Copy, sandboxDir, outputDir, rawCommand string) error {
429*333d2b36SAndroid Build Coastguard Worker	var missingOutputErrors []error
430*333d2b36SAndroid Build Coastguard Worker	var incorrectOutputDirectoryErrors []error
431*333d2b36SAndroid Build Coastguard Worker	for _, copyPair := range copies {
432*333d2b36SAndroid Build Coastguard Worker		fromPath := joinPath(sandboxDir, copyPair.GetFrom())
433*333d2b36SAndroid Build Coastguard Worker		fileInfo, err := os.Stat(fromPath)
434*333d2b36SAndroid Build Coastguard Worker		if err != nil {
435*333d2b36SAndroid Build Coastguard Worker			missingOutputErrors = append(missingOutputErrors, fmt.Errorf("%s: does not exist", fromPath))
436*333d2b36SAndroid Build Coastguard Worker			continue
437*333d2b36SAndroid Build Coastguard Worker		}
438*333d2b36SAndroid Build Coastguard Worker		if fileInfo.IsDir() {
439*333d2b36SAndroid Build Coastguard Worker			missingOutputErrors = append(missingOutputErrors, fmt.Errorf("%s: not a file", fromPath))
440*333d2b36SAndroid Build Coastguard Worker		}
441*333d2b36SAndroid Build Coastguard Worker
442*333d2b36SAndroid Build Coastguard Worker		toPath := copyPair.GetTo()
443*333d2b36SAndroid Build Coastguard Worker		if rel, err := filepath.Rel(outputDir, toPath); err != nil {
444*333d2b36SAndroid Build Coastguard Worker			return err
445*333d2b36SAndroid Build Coastguard Worker		} else if strings.HasPrefix(rel, "../") {
446*333d2b36SAndroid Build Coastguard Worker			incorrectOutputDirectoryErrors = append(incorrectOutputDirectoryErrors,
447*333d2b36SAndroid Build Coastguard Worker				fmt.Errorf("%s is not under %s", toPath, outputDir))
448*333d2b36SAndroid Build Coastguard Worker		}
449*333d2b36SAndroid Build Coastguard Worker	}
450*333d2b36SAndroid Build Coastguard Worker
451*333d2b36SAndroid Build Coastguard Worker	const maxErrors = 25
452*333d2b36SAndroid Build Coastguard Worker
453*333d2b36SAndroid Build Coastguard Worker	if len(incorrectOutputDirectoryErrors) > 0 {
454*333d2b36SAndroid Build Coastguard Worker		errorMessage := ""
455*333d2b36SAndroid Build Coastguard Worker		more := 0
456*333d2b36SAndroid Build Coastguard Worker		if len(incorrectOutputDirectoryErrors) > maxErrors {
457*333d2b36SAndroid Build Coastguard Worker			more = len(incorrectOutputDirectoryErrors) - maxErrors
458*333d2b36SAndroid Build Coastguard Worker			incorrectOutputDirectoryErrors = incorrectOutputDirectoryErrors[:maxErrors]
459*333d2b36SAndroid Build Coastguard Worker		}
460*333d2b36SAndroid Build Coastguard Worker
461*333d2b36SAndroid Build Coastguard Worker		for _, err := range incorrectOutputDirectoryErrors {
462*333d2b36SAndroid Build Coastguard Worker			errorMessage += err.Error() + "\n"
463*333d2b36SAndroid Build Coastguard Worker		}
464*333d2b36SAndroid Build Coastguard Worker		if more > 0 {
465*333d2b36SAndroid Build Coastguard Worker			errorMessage += fmt.Sprintf("...%v more", more)
466*333d2b36SAndroid Build Coastguard Worker		}
467*333d2b36SAndroid Build Coastguard Worker
468*333d2b36SAndroid Build Coastguard Worker		return errors.New(errorMessage)
469*333d2b36SAndroid Build Coastguard Worker	}
470*333d2b36SAndroid Build Coastguard Worker
471*333d2b36SAndroid Build Coastguard Worker	if len(missingOutputErrors) > 0 {
472*333d2b36SAndroid Build Coastguard Worker		// find all created files for making a more informative error message
473*333d2b36SAndroid Build Coastguard Worker		createdFiles := findAllFilesUnder(sandboxDir)
474*333d2b36SAndroid Build Coastguard Worker
475*333d2b36SAndroid Build Coastguard Worker		// build error message
476*333d2b36SAndroid Build Coastguard Worker		errorMessage := "mismatch between declared and actual outputs\n"
477*333d2b36SAndroid Build Coastguard Worker		errorMessage += "in sbox command(" + rawCommand + ")\n\n"
478*333d2b36SAndroid Build Coastguard Worker		errorMessage += "in sandbox " + sandboxDir + ",\n"
479*333d2b36SAndroid Build Coastguard Worker		errorMessage += fmt.Sprintf("failed to create %v files:\n", len(missingOutputErrors))
480*333d2b36SAndroid Build Coastguard Worker		for _, missingOutputError := range missingOutputErrors {
481*333d2b36SAndroid Build Coastguard Worker			errorMessage += "  " + missingOutputError.Error() + "\n"
482*333d2b36SAndroid Build Coastguard Worker		}
483*333d2b36SAndroid Build Coastguard Worker		if len(createdFiles) < 1 {
484*333d2b36SAndroid Build Coastguard Worker			errorMessage += "created 0 files."
485*333d2b36SAndroid Build Coastguard Worker		} else {
486*333d2b36SAndroid Build Coastguard Worker			errorMessage += fmt.Sprintf("did create %v files:\n", len(createdFiles))
487*333d2b36SAndroid Build Coastguard Worker			creationMessages := createdFiles
488*333d2b36SAndroid Build Coastguard Worker			if len(creationMessages) > maxErrors {
489*333d2b36SAndroid Build Coastguard Worker				creationMessages = creationMessages[:maxErrors]
490*333d2b36SAndroid Build Coastguard Worker				creationMessages = append(creationMessages, fmt.Sprintf("...%v more", len(createdFiles)-maxErrors))
491*333d2b36SAndroid Build Coastguard Worker			}
492*333d2b36SAndroid Build Coastguard Worker			for _, creationMessage := range creationMessages {
493*333d2b36SAndroid Build Coastguard Worker				errorMessage += "  " + creationMessage + "\n"
494*333d2b36SAndroid Build Coastguard Worker			}
495*333d2b36SAndroid Build Coastguard Worker		}
496*333d2b36SAndroid Build Coastguard Worker
497*333d2b36SAndroid Build Coastguard Worker		return errors.New(errorMessage)
498*333d2b36SAndroid Build Coastguard Worker	}
499*333d2b36SAndroid Build Coastguard Worker
500*333d2b36SAndroid Build Coastguard Worker	return nil
501*333d2b36SAndroid Build Coastguard Worker}
502*333d2b36SAndroid Build Coastguard Worker
503*333d2b36SAndroid Build Coastguard Workertype existsType bool
504*333d2b36SAndroid Build Coastguard Worker
505*333d2b36SAndroid Build Coastguard Workerconst (
506*333d2b36SAndroid Build Coastguard Worker	requireFromExists  existsType = false
507*333d2b36SAndroid Build Coastguard Worker	allowFromNotExists            = true
508*333d2b36SAndroid Build Coastguard Worker)
509*333d2b36SAndroid Build Coastguard Worker
510*333d2b36SAndroid Build Coastguard Workertype writeType bool
511*333d2b36SAndroid Build Coastguard Worker
512*333d2b36SAndroid Build Coastguard Workerconst (
513*333d2b36SAndroid Build Coastguard Worker	alwaysWrite        writeType = false
514*333d2b36SAndroid Build Coastguard Worker	onlyWriteIfChanged           = true
515*333d2b36SAndroid Build Coastguard Worker)
516*333d2b36SAndroid Build Coastguard Worker
517*333d2b36SAndroid Build Coastguard Worker// copyFiles copies files in or out of the sandbox.  If exists is allowFromNotExists then errors
518*333d2b36SAndroid Build Coastguard Worker// caused by a from path not existing are ignored.  If write is onlyWriteIfChanged then the output
519*333d2b36SAndroid Build Coastguard Worker// file is compared to the input file and not written to if it is the same, avoiding updating
520*333d2b36SAndroid Build Coastguard Worker// the timestamp.
521*333d2b36SAndroid Build Coastguard Workerfunc copyFiles(copies []*sbox_proto.Copy, fromDir, toDir string, exists existsType, write writeType) error {
522*333d2b36SAndroid Build Coastguard Worker	for _, copyPair := range copies {
523*333d2b36SAndroid Build Coastguard Worker		fromPath := joinPath(fromDir, copyPair.GetFrom())
524*333d2b36SAndroid Build Coastguard Worker		toPath := joinPath(toDir, copyPair.GetTo())
525*333d2b36SAndroid Build Coastguard Worker		err := copyOneFile(fromPath, toPath, copyPair.GetExecutable(), exists, write)
526*333d2b36SAndroid Build Coastguard Worker		if err != nil {
527*333d2b36SAndroid Build Coastguard Worker			return fmt.Errorf("error copying %q to %q: %w", fromPath, toPath, err)
528*333d2b36SAndroid Build Coastguard Worker		}
529*333d2b36SAndroid Build Coastguard Worker	}
530*333d2b36SAndroid Build Coastguard Worker	return nil
531*333d2b36SAndroid Build Coastguard Worker}
532*333d2b36SAndroid Build Coastguard Worker
533*333d2b36SAndroid Build Coastguard Worker// copyOneFile copies a file and its permissions.  If forceExecutable is true it adds u+x to the
534*333d2b36SAndroid Build Coastguard Worker// permissions.  If exists is allowFromNotExists it returns nil if the from path doesn't exist.
535*333d2b36SAndroid Build Coastguard Worker// If write is onlyWriteIfChanged then the output file is compared to the input file and not written to
536*333d2b36SAndroid Build Coastguard Worker// if it is the same, avoiding updating the timestamp. If from is a symlink, the symlink itself
537*333d2b36SAndroid Build Coastguard Worker// will be copied, instead of what it points to.
538*333d2b36SAndroid Build Coastguard Workerfunc copyOneFile(from string, to string, forceExecutable bool, exists existsType,
539*333d2b36SAndroid Build Coastguard Worker	write writeType) error {
540*333d2b36SAndroid Build Coastguard Worker	err := os.MkdirAll(filepath.Dir(to), 0777)
541*333d2b36SAndroid Build Coastguard Worker	if err != nil {
542*333d2b36SAndroid Build Coastguard Worker		return err
543*333d2b36SAndroid Build Coastguard Worker	}
544*333d2b36SAndroid Build Coastguard Worker
545*333d2b36SAndroid Build Coastguard Worker	stat, err := os.Lstat(from)
546*333d2b36SAndroid Build Coastguard Worker	if err != nil {
547*333d2b36SAndroid Build Coastguard Worker		if os.IsNotExist(err) && exists == allowFromNotExists {
548*333d2b36SAndroid Build Coastguard Worker			return nil
549*333d2b36SAndroid Build Coastguard Worker		}
550*333d2b36SAndroid Build Coastguard Worker		return err
551*333d2b36SAndroid Build Coastguard Worker	}
552*333d2b36SAndroid Build Coastguard Worker
553*333d2b36SAndroid Build Coastguard Worker	if stat.Mode()&fs.ModeSymlink != 0 {
554*333d2b36SAndroid Build Coastguard Worker		linkTarget, err := os.Readlink(from)
555*333d2b36SAndroid Build Coastguard Worker		if err != nil {
556*333d2b36SAndroid Build Coastguard Worker			return err
557*333d2b36SAndroid Build Coastguard Worker		}
558*333d2b36SAndroid Build Coastguard Worker		if write == onlyWriteIfChanged {
559*333d2b36SAndroid Build Coastguard Worker			toLinkTarget, err := os.Readlink(to)
560*333d2b36SAndroid Build Coastguard Worker			if err == nil && toLinkTarget == linkTarget {
561*333d2b36SAndroid Build Coastguard Worker				return nil
562*333d2b36SAndroid Build Coastguard Worker			}
563*333d2b36SAndroid Build Coastguard Worker		}
564*333d2b36SAndroid Build Coastguard Worker		err = os.Remove(to)
565*333d2b36SAndroid Build Coastguard Worker		if err != nil && !os.IsNotExist(err) {
566*333d2b36SAndroid Build Coastguard Worker			return err
567*333d2b36SAndroid Build Coastguard Worker		}
568*333d2b36SAndroid Build Coastguard Worker
569*333d2b36SAndroid Build Coastguard Worker		return os.Symlink(linkTarget, to)
570*333d2b36SAndroid Build Coastguard Worker	}
571*333d2b36SAndroid Build Coastguard Worker
572*333d2b36SAndroid Build Coastguard Worker	perm := stat.Mode()
573*333d2b36SAndroid Build Coastguard Worker	if forceExecutable {
574*333d2b36SAndroid Build Coastguard Worker		perm = perm | 0100 // u+x
575*333d2b36SAndroid Build Coastguard Worker	}
576*333d2b36SAndroid Build Coastguard Worker
577*333d2b36SAndroid Build Coastguard Worker	if write == onlyWriteIfChanged && filesHaveSameContents(from, to) {
578*333d2b36SAndroid Build Coastguard Worker		return nil
579*333d2b36SAndroid Build Coastguard Worker	}
580*333d2b36SAndroid Build Coastguard Worker
581*333d2b36SAndroid Build Coastguard Worker	in, err := os.Open(from)
582*333d2b36SAndroid Build Coastguard Worker	if err != nil {
583*333d2b36SAndroid Build Coastguard Worker		return err
584*333d2b36SAndroid Build Coastguard Worker	}
585*333d2b36SAndroid Build Coastguard Worker	defer in.Close()
586*333d2b36SAndroid Build Coastguard Worker
587*333d2b36SAndroid Build Coastguard Worker	// Remove the target before copying.  In most cases the file won't exist, but if there are
588*333d2b36SAndroid Build Coastguard Worker	// duplicate copy rules for a file and the source file was read-only the second copy could
589*333d2b36SAndroid Build Coastguard Worker	// fail.
590*333d2b36SAndroid Build Coastguard Worker	err = os.Remove(to)
591*333d2b36SAndroid Build Coastguard Worker	if err != nil && !os.IsNotExist(err) {
592*333d2b36SAndroid Build Coastguard Worker		return err
593*333d2b36SAndroid Build Coastguard Worker	}
594*333d2b36SAndroid Build Coastguard Worker
595*333d2b36SAndroid Build Coastguard Worker	out, err := os.Create(to)
596*333d2b36SAndroid Build Coastguard Worker	if err != nil {
597*333d2b36SAndroid Build Coastguard Worker		return err
598*333d2b36SAndroid Build Coastguard Worker	}
599*333d2b36SAndroid Build Coastguard Worker	defer func() {
600*333d2b36SAndroid Build Coastguard Worker		out.Close()
601*333d2b36SAndroid Build Coastguard Worker		if err != nil {
602*333d2b36SAndroid Build Coastguard Worker			os.Remove(to)
603*333d2b36SAndroid Build Coastguard Worker		}
604*333d2b36SAndroid Build Coastguard Worker	}()
605*333d2b36SAndroid Build Coastguard Worker
606*333d2b36SAndroid Build Coastguard Worker	_, err = io.Copy(out, in)
607*333d2b36SAndroid Build Coastguard Worker	if err != nil {
608*333d2b36SAndroid Build Coastguard Worker		return err
609*333d2b36SAndroid Build Coastguard Worker	}
610*333d2b36SAndroid Build Coastguard Worker
611*333d2b36SAndroid Build Coastguard Worker	if err = out.Close(); err != nil {
612*333d2b36SAndroid Build Coastguard Worker		return err
613*333d2b36SAndroid Build Coastguard Worker	}
614*333d2b36SAndroid Build Coastguard Worker
615*333d2b36SAndroid Build Coastguard Worker	if err = os.Chmod(to, perm); err != nil {
616*333d2b36SAndroid Build Coastguard Worker		return err
617*333d2b36SAndroid Build Coastguard Worker	}
618*333d2b36SAndroid Build Coastguard Worker
619*333d2b36SAndroid Build Coastguard Worker	return nil
620*333d2b36SAndroid Build Coastguard Worker}
621*333d2b36SAndroid Build Coastguard Worker
622*333d2b36SAndroid Build Coastguard Worker// copyRspFiles copies rsp files into the sandbox with path mappings, and also copies the files
623*333d2b36SAndroid Build Coastguard Worker// listed into the sandbox.
624*333d2b36SAndroid Build Coastguard Workerfunc copyRspFiles(rspFiles []*sbox_proto.RspFile, toDir, toDirInSandbox string) error {
625*333d2b36SAndroid Build Coastguard Worker	for _, rspFile := range rspFiles {
626*333d2b36SAndroid Build Coastguard Worker		err := copyOneRspFile(rspFile, toDir, toDirInSandbox)
627*333d2b36SAndroid Build Coastguard Worker		if err != nil {
628*333d2b36SAndroid Build Coastguard Worker			return err
629*333d2b36SAndroid Build Coastguard Worker		}
630*333d2b36SAndroid Build Coastguard Worker	}
631*333d2b36SAndroid Build Coastguard Worker	return nil
632*333d2b36SAndroid Build Coastguard Worker}
633*333d2b36SAndroid Build Coastguard Worker
634*333d2b36SAndroid Build Coastguard Worker// copyOneRspFiles copies an rsp file into the sandbox with path mappings, and also copies the files
635*333d2b36SAndroid Build Coastguard Worker// listed into the sandbox.
636*333d2b36SAndroid Build Coastguard Workerfunc copyOneRspFile(rspFile *sbox_proto.RspFile, toDir, toDirInSandbox string) error {
637*333d2b36SAndroid Build Coastguard Worker	in, err := os.Open(rspFile.GetFile())
638*333d2b36SAndroid Build Coastguard Worker	if err != nil {
639*333d2b36SAndroid Build Coastguard Worker		return err
640*333d2b36SAndroid Build Coastguard Worker	}
641*333d2b36SAndroid Build Coastguard Worker	defer in.Close()
642*333d2b36SAndroid Build Coastguard Worker
643*333d2b36SAndroid Build Coastguard Worker	files, err := response.ReadRspFile(in)
644*333d2b36SAndroid Build Coastguard Worker	if err != nil {
645*333d2b36SAndroid Build Coastguard Worker		return err
646*333d2b36SAndroid Build Coastguard Worker	}
647*333d2b36SAndroid Build Coastguard Worker
648*333d2b36SAndroid Build Coastguard Worker	for i, from := range files {
649*333d2b36SAndroid Build Coastguard Worker		// Convert the real path of the input file into the path inside the sandbox using the
650*333d2b36SAndroid Build Coastguard Worker		// path mappings.
651*333d2b36SAndroid Build Coastguard Worker		to := applyPathMappings(rspFile.PathMappings, from)
652*333d2b36SAndroid Build Coastguard Worker
653*333d2b36SAndroid Build Coastguard Worker		// Copy the file into the sandbox.
654*333d2b36SAndroid Build Coastguard Worker		err := copyOneFile(from, joinPath(toDir, to), false, requireFromExists, alwaysWrite)
655*333d2b36SAndroid Build Coastguard Worker		if err != nil {
656*333d2b36SAndroid Build Coastguard Worker			return err
657*333d2b36SAndroid Build Coastguard Worker		}
658*333d2b36SAndroid Build Coastguard Worker
659*333d2b36SAndroid Build Coastguard Worker		// Rewrite the name in the list of files to be relative to the sandbox directory.
660*333d2b36SAndroid Build Coastguard Worker		files[i] = joinPath(toDirInSandbox, to)
661*333d2b36SAndroid Build Coastguard Worker	}
662*333d2b36SAndroid Build Coastguard Worker
663*333d2b36SAndroid Build Coastguard Worker	// Convert the real path of the rsp file into the path inside the sandbox using the path
664*333d2b36SAndroid Build Coastguard Worker	// mappings.
665*333d2b36SAndroid Build Coastguard Worker	outRspFile := joinPath(toDir, applyPathMappings(rspFile.PathMappings, rspFile.GetFile()))
666*333d2b36SAndroid Build Coastguard Worker
667*333d2b36SAndroid Build Coastguard Worker	err = os.MkdirAll(filepath.Dir(outRspFile), 0777)
668*333d2b36SAndroid Build Coastguard Worker	if err != nil {
669*333d2b36SAndroid Build Coastguard Worker		return err
670*333d2b36SAndroid Build Coastguard Worker	}
671*333d2b36SAndroid Build Coastguard Worker
672*333d2b36SAndroid Build Coastguard Worker	out, err := os.Create(outRspFile)
673*333d2b36SAndroid Build Coastguard Worker	if err != nil {
674*333d2b36SAndroid Build Coastguard Worker		return err
675*333d2b36SAndroid Build Coastguard Worker	}
676*333d2b36SAndroid Build Coastguard Worker	defer out.Close()
677*333d2b36SAndroid Build Coastguard Worker
678*333d2b36SAndroid Build Coastguard Worker	// Write the rsp file with converted paths into the sandbox.
679*333d2b36SAndroid Build Coastguard Worker	err = response.WriteRspFile(out, files)
680*333d2b36SAndroid Build Coastguard Worker	if err != nil {
681*333d2b36SAndroid Build Coastguard Worker		return err
682*333d2b36SAndroid Build Coastguard Worker	}
683*333d2b36SAndroid Build Coastguard Worker
684*333d2b36SAndroid Build Coastguard Worker	return nil
685*333d2b36SAndroid Build Coastguard Worker}
686*333d2b36SAndroid Build Coastguard Worker
687*333d2b36SAndroid Build Coastguard Worker// applyPathMappings takes a list of path mappings and a path, and returns the path with the first
688*333d2b36SAndroid Build Coastguard Worker// matching path mapping applied.  If the path does not match any of the path mappings then it is
689*333d2b36SAndroid Build Coastguard Worker// returned unmodified.
690*333d2b36SAndroid Build Coastguard Workerfunc applyPathMappings(pathMappings []*sbox_proto.PathMapping, path string) string {
691*333d2b36SAndroid Build Coastguard Worker	for _, mapping := range pathMappings {
692*333d2b36SAndroid Build Coastguard Worker		if strings.HasPrefix(path, mapping.GetFrom()+"/") {
693*333d2b36SAndroid Build Coastguard Worker			return joinPath(mapping.GetTo()+"/", strings.TrimPrefix(path, mapping.GetFrom()+"/"))
694*333d2b36SAndroid Build Coastguard Worker		}
695*333d2b36SAndroid Build Coastguard Worker	}
696*333d2b36SAndroid Build Coastguard Worker	return path
697*333d2b36SAndroid Build Coastguard Worker}
698*333d2b36SAndroid Build Coastguard Worker
699*333d2b36SAndroid Build Coastguard Worker// moveFiles moves files specified by a set of copy rules.  It uses os.Rename, so it is restricted
700*333d2b36SAndroid Build Coastguard Worker// to moving files where the source and destination are in the same filesystem.  This is OK for
701*333d2b36SAndroid Build Coastguard Worker// sbox because the temporary directory is inside the out directory.  If write is onlyWriteIfChanged
702*333d2b36SAndroid Build Coastguard Worker// then the output file is compared to the input file and not written to if it is the same, avoiding
703*333d2b36SAndroid Build Coastguard Worker// updating the timestamp.  Otherwise it always updates the timestamp of the new file.
704*333d2b36SAndroid Build Coastguard Workerfunc moveFiles(copies []*sbox_proto.Copy, fromDir, toDir string, write writeType) error {
705*333d2b36SAndroid Build Coastguard Worker	for _, copyPair := range copies {
706*333d2b36SAndroid Build Coastguard Worker		fromPath := joinPath(fromDir, copyPair.GetFrom())
707*333d2b36SAndroid Build Coastguard Worker		toPath := joinPath(toDir, copyPair.GetTo())
708*333d2b36SAndroid Build Coastguard Worker		err := os.MkdirAll(filepath.Dir(toPath), 0777)
709*333d2b36SAndroid Build Coastguard Worker		if err != nil {
710*333d2b36SAndroid Build Coastguard Worker			return err
711*333d2b36SAndroid Build Coastguard Worker		}
712*333d2b36SAndroid Build Coastguard Worker
713*333d2b36SAndroid Build Coastguard Worker		if write == onlyWriteIfChanged && filesHaveSameContents(fromPath, toPath) {
714*333d2b36SAndroid Build Coastguard Worker			continue
715*333d2b36SAndroid Build Coastguard Worker		}
716*333d2b36SAndroid Build Coastguard Worker
717*333d2b36SAndroid Build Coastguard Worker		err = os.Rename(fromPath, toPath)
718*333d2b36SAndroid Build Coastguard Worker		if err != nil {
719*333d2b36SAndroid Build Coastguard Worker			return err
720*333d2b36SAndroid Build Coastguard Worker		}
721*333d2b36SAndroid Build Coastguard Worker
722*333d2b36SAndroid Build Coastguard Worker		// Update the timestamp of the output file in case the tool wrote an old timestamp (for example, tar can extract
723*333d2b36SAndroid Build Coastguard Worker		// files with old timestamps).
724*333d2b36SAndroid Build Coastguard Worker		now := time.Now()
725*333d2b36SAndroid Build Coastguard Worker		err = os.Chtimes(toPath, now, now)
726*333d2b36SAndroid Build Coastguard Worker		if err != nil {
727*333d2b36SAndroid Build Coastguard Worker			return err
728*333d2b36SAndroid Build Coastguard Worker		}
729*333d2b36SAndroid Build Coastguard Worker	}
730*333d2b36SAndroid Build Coastguard Worker	return nil
731*333d2b36SAndroid Build Coastguard Worker}
732*333d2b36SAndroid Build Coastguard Worker
733*333d2b36SAndroid Build Coastguard Worker// clearOutputDirectory removes all files in the output directory if write is alwaysWrite, or
734*333d2b36SAndroid Build Coastguard Worker// any files not listed in copies if write is onlyWriteIfChanged
735*333d2b36SAndroid Build Coastguard Workerfunc clearOutputDirectory(copies []*sbox_proto.Copy, outputDir string, write writeType) error {
736*333d2b36SAndroid Build Coastguard Worker	if outputDir == "" {
737*333d2b36SAndroid Build Coastguard Worker		return fmt.Errorf("output directory must be set")
738*333d2b36SAndroid Build Coastguard Worker	}
739*333d2b36SAndroid Build Coastguard Worker
740*333d2b36SAndroid Build Coastguard Worker	if write == alwaysWrite {
741*333d2b36SAndroid Build Coastguard Worker		// When writing all the output files remove the whole output directory
742*333d2b36SAndroid Build Coastguard Worker		return os.RemoveAll(outputDir)
743*333d2b36SAndroid Build Coastguard Worker	}
744*333d2b36SAndroid Build Coastguard Worker
745*333d2b36SAndroid Build Coastguard Worker	outputFiles := make(map[string]bool, len(copies))
746*333d2b36SAndroid Build Coastguard Worker	for _, copyPair := range copies {
747*333d2b36SAndroid Build Coastguard Worker		outputFiles[copyPair.GetTo()] = true
748*333d2b36SAndroid Build Coastguard Worker	}
749*333d2b36SAndroid Build Coastguard Worker
750*333d2b36SAndroid Build Coastguard Worker	existingFiles := findAllFilesUnder(outputDir)
751*333d2b36SAndroid Build Coastguard Worker	for _, existingFile := range existingFiles {
752*333d2b36SAndroid Build Coastguard Worker		fullExistingFile := filepath.Join(outputDir, existingFile)
753*333d2b36SAndroid Build Coastguard Worker		if !outputFiles[fullExistingFile] {
754*333d2b36SAndroid Build Coastguard Worker			err := os.Remove(fullExistingFile)
755*333d2b36SAndroid Build Coastguard Worker			if err != nil {
756*333d2b36SAndroid Build Coastguard Worker				return fmt.Errorf("failed to remove obsolete output file %s: %w", fullExistingFile, err)
757*333d2b36SAndroid Build Coastguard Worker			}
758*333d2b36SAndroid Build Coastguard Worker		}
759*333d2b36SAndroid Build Coastguard Worker	}
760*333d2b36SAndroid Build Coastguard Worker
761*333d2b36SAndroid Build Coastguard Worker	return nil
762*333d2b36SAndroid Build Coastguard Worker}
763*333d2b36SAndroid Build Coastguard Worker
764*333d2b36SAndroid Build Coastguard Worker// Rewrite one or more depfiles so that it doesn't include the (randomized) sandbox directory
765*333d2b36SAndroid Build Coastguard Worker// to an output file.
766*333d2b36SAndroid Build Coastguard Workerfunc rewriteDepFiles(ins []string, out string) error {
767*333d2b36SAndroid Build Coastguard Worker	var mergedDeps []string
768*333d2b36SAndroid Build Coastguard Worker	for _, in := range ins {
769*333d2b36SAndroid Build Coastguard Worker		data, err := ioutil.ReadFile(in)
770*333d2b36SAndroid Build Coastguard Worker		if err != nil {
771*333d2b36SAndroid Build Coastguard Worker			return err
772*333d2b36SAndroid Build Coastguard Worker		}
773*333d2b36SAndroid Build Coastguard Worker
774*333d2b36SAndroid Build Coastguard Worker		deps, err := makedeps.Parse(in, bytes.NewBuffer(data))
775*333d2b36SAndroid Build Coastguard Worker		if err != nil {
776*333d2b36SAndroid Build Coastguard Worker			return err
777*333d2b36SAndroid Build Coastguard Worker		}
778*333d2b36SAndroid Build Coastguard Worker		mergedDeps = append(mergedDeps, deps.Inputs...)
779*333d2b36SAndroid Build Coastguard Worker	}
780*333d2b36SAndroid Build Coastguard Worker
781*333d2b36SAndroid Build Coastguard Worker	deps := makedeps.Deps{
782*333d2b36SAndroid Build Coastguard Worker		// Ninja doesn't care what the output file is, so we can use any string here.
783*333d2b36SAndroid Build Coastguard Worker		Output: "outputfile",
784*333d2b36SAndroid Build Coastguard Worker		Inputs: mergedDeps,
785*333d2b36SAndroid Build Coastguard Worker	}
786*333d2b36SAndroid Build Coastguard Worker
787*333d2b36SAndroid Build Coastguard Worker	// Make the directory for the output depfile in case it is in a different directory
788*333d2b36SAndroid Build Coastguard Worker	// than any of the output files.
789*333d2b36SAndroid Build Coastguard Worker	outDir := filepath.Dir(out)
790*333d2b36SAndroid Build Coastguard Worker	err := os.MkdirAll(outDir, 0777)
791*333d2b36SAndroid Build Coastguard Worker	if err != nil {
792*333d2b36SAndroid Build Coastguard Worker		return fmt.Errorf("failed to create %q: %w", outDir, err)
793*333d2b36SAndroid Build Coastguard Worker	}
794*333d2b36SAndroid Build Coastguard Worker
795*333d2b36SAndroid Build Coastguard Worker	return ioutil.WriteFile(out, deps.Print(), 0666)
796*333d2b36SAndroid Build Coastguard Worker}
797*333d2b36SAndroid Build Coastguard Worker
798*333d2b36SAndroid Build Coastguard Worker// joinPath wraps filepath.Join but returns file without appending to dir if file is
799*333d2b36SAndroid Build Coastguard Worker// absolute.
800*333d2b36SAndroid Build Coastguard Workerfunc joinPath(dir, file string) string {
801*333d2b36SAndroid Build Coastguard Worker	if filepath.IsAbs(file) {
802*333d2b36SAndroid Build Coastguard Worker		return file
803*333d2b36SAndroid Build Coastguard Worker	}
804*333d2b36SAndroid Build Coastguard Worker	return filepath.Join(dir, file)
805*333d2b36SAndroid Build Coastguard Worker}
806*333d2b36SAndroid Build Coastguard Worker
807*333d2b36SAndroid Build Coastguard Worker// filesHaveSameContents compares the contents if two files, returning true if they are the same
808*333d2b36SAndroid Build Coastguard Worker// and returning false if they are different or any errors occur.
809*333d2b36SAndroid Build Coastguard Workerfunc filesHaveSameContents(a, b string) bool {
810*333d2b36SAndroid Build Coastguard Worker	// Compare the sizes of the two files
811*333d2b36SAndroid Build Coastguard Worker	statA, err := os.Stat(a)
812*333d2b36SAndroid Build Coastguard Worker	if err != nil {
813*333d2b36SAndroid Build Coastguard Worker		return false
814*333d2b36SAndroid Build Coastguard Worker	}
815*333d2b36SAndroid Build Coastguard Worker	statB, err := os.Stat(b)
816*333d2b36SAndroid Build Coastguard Worker	if err != nil {
817*333d2b36SAndroid Build Coastguard Worker		return false
818*333d2b36SAndroid Build Coastguard Worker	}
819*333d2b36SAndroid Build Coastguard Worker
820*333d2b36SAndroid Build Coastguard Worker	if statA.Size() != statB.Size() {
821*333d2b36SAndroid Build Coastguard Worker		return false
822*333d2b36SAndroid Build Coastguard Worker	}
823*333d2b36SAndroid Build Coastguard Worker
824*333d2b36SAndroid Build Coastguard Worker	// Open the two files
825*333d2b36SAndroid Build Coastguard Worker	fileA, err := os.Open(a)
826*333d2b36SAndroid Build Coastguard Worker	if err != nil {
827*333d2b36SAndroid Build Coastguard Worker		return false
828*333d2b36SAndroid Build Coastguard Worker	}
829*333d2b36SAndroid Build Coastguard Worker	defer fileA.Close()
830*333d2b36SAndroid Build Coastguard Worker	fileB, err := os.Open(b)
831*333d2b36SAndroid Build Coastguard Worker	if err != nil {
832*333d2b36SAndroid Build Coastguard Worker		return false
833*333d2b36SAndroid Build Coastguard Worker	}
834*333d2b36SAndroid Build Coastguard Worker	defer fileB.Close()
835*333d2b36SAndroid Build Coastguard Worker
836*333d2b36SAndroid Build Coastguard Worker	// Compare the files 1MB at a time
837*333d2b36SAndroid Build Coastguard Worker	const bufSize = 1 * 1024 * 1024
838*333d2b36SAndroid Build Coastguard Worker	bufA := make([]byte, bufSize)
839*333d2b36SAndroid Build Coastguard Worker	bufB := make([]byte, bufSize)
840*333d2b36SAndroid Build Coastguard Worker
841*333d2b36SAndroid Build Coastguard Worker	remain := statA.Size()
842*333d2b36SAndroid Build Coastguard Worker	for remain > 0 {
843*333d2b36SAndroid Build Coastguard Worker		toRead := int64(bufSize)
844*333d2b36SAndroid Build Coastguard Worker		if toRead > remain {
845*333d2b36SAndroid Build Coastguard Worker			toRead = remain
846*333d2b36SAndroid Build Coastguard Worker		}
847*333d2b36SAndroid Build Coastguard Worker
848*333d2b36SAndroid Build Coastguard Worker		_, err = io.ReadFull(fileA, bufA[:toRead])
849*333d2b36SAndroid Build Coastguard Worker		if err != nil {
850*333d2b36SAndroid Build Coastguard Worker			return false
851*333d2b36SAndroid Build Coastguard Worker		}
852*333d2b36SAndroid Build Coastguard Worker		_, err = io.ReadFull(fileB, bufB[:toRead])
853*333d2b36SAndroid Build Coastguard Worker		if err != nil {
854*333d2b36SAndroid Build Coastguard Worker			return false
855*333d2b36SAndroid Build Coastguard Worker		}
856*333d2b36SAndroid Build Coastguard Worker
857*333d2b36SAndroid Build Coastguard Worker		if bytes.Compare(bufA[:toRead], bufB[:toRead]) != 0 {
858*333d2b36SAndroid Build Coastguard Worker			return false
859*333d2b36SAndroid Build Coastguard Worker		}
860*333d2b36SAndroid Build Coastguard Worker
861*333d2b36SAndroid Build Coastguard Worker		remain -= toRead
862*333d2b36SAndroid Build Coastguard Worker	}
863*333d2b36SAndroid Build Coastguard Worker
864*333d2b36SAndroid Build Coastguard Worker	return true
865*333d2b36SAndroid Build Coastguard Worker}
866*333d2b36SAndroid Build Coastguard Worker
867*333d2b36SAndroid Build Coastguard Workerfunc makeAbsPathEnv(pathEnv string) (string, error) {
868*333d2b36SAndroid Build Coastguard Worker	pathEnvElements := filepath.SplitList(pathEnv)
869*333d2b36SAndroid Build Coastguard Worker	for i, p := range pathEnvElements {
870*333d2b36SAndroid Build Coastguard Worker		if !filepath.IsAbs(p) {
871*333d2b36SAndroid Build Coastguard Worker			absPath, err := filepath.Abs(p)
872*333d2b36SAndroid Build Coastguard Worker			if err != nil {
873*333d2b36SAndroid Build Coastguard Worker				return "", fmt.Errorf("failed to make PATH entry %q absolute: %w", p, err)
874*333d2b36SAndroid Build Coastguard Worker			}
875*333d2b36SAndroid Build Coastguard Worker			pathEnvElements[i] = absPath
876*333d2b36SAndroid Build Coastguard Worker		}
877*333d2b36SAndroid Build Coastguard Worker	}
878*333d2b36SAndroid Build Coastguard Worker	return strings.Join(pathEnvElements, string(filepath.ListSeparator)), nil
879*333d2b36SAndroid Build Coastguard Worker}
880