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