1// Copyright 2019 The Go Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style 3// license that can be found in the LICENSE file. 4 5package bootstrap_test 6 7import ( 8 "io" 9 "io/fs" 10 "os" 11 "path/filepath" 12 "strings" 13) 14 15// overlayDir makes a minimal-overhead copy of srcRoot in which new files may be added. 16// 17// TODO: Once we no longer need to support the misc module in GOPATH mode, 18// factor this function out into a package to reduce duplication. 19func overlayDir(dstRoot, srcRoot string) error { 20 dstRoot = filepath.Clean(dstRoot) 21 if err := os.MkdirAll(dstRoot, 0777); err != nil { 22 return err 23 } 24 25 srcRoot, err := filepath.Abs(srcRoot) 26 if err != nil { 27 return err 28 } 29 30 return filepath.WalkDir(srcRoot, func(srcPath string, entry fs.DirEntry, err error) error { 31 if err != nil || srcPath == srcRoot { 32 return err 33 } 34 if filepath.Base(srcPath) == "testdata" { 35 // We're just building, so no need to copy those. 36 return fs.SkipDir 37 } 38 39 suffix := strings.TrimPrefix(srcPath, srcRoot) 40 for len(suffix) > 0 && suffix[0] == filepath.Separator { 41 suffix = suffix[1:] 42 } 43 dstPath := filepath.Join(dstRoot, suffix) 44 45 info, err := entry.Info() 46 perm := info.Mode() & os.ModePerm 47 if info.Mode()&os.ModeSymlink != 0 { 48 info, err = os.Stat(srcPath) 49 if err != nil { 50 return err 51 } 52 perm = info.Mode() & os.ModePerm 53 } 54 55 // Always make copies of directories. 56 // If we add a file in the overlay, we don't want to add it in the original. 57 if info.IsDir() { 58 return os.MkdirAll(dstPath, perm|0200) 59 } 60 61 // If we can use a hard link, do that instead of copying bytes. 62 // Go builds don't like symlinks in some cases, such as go:embed. 63 if err := os.Link(srcPath, dstPath); err == nil { 64 return nil 65 } 66 67 // Otherwise, copy the bytes. 68 src, err := os.Open(srcPath) 69 if err != nil { 70 return err 71 } 72 defer src.Close() 73 74 dst, err := os.OpenFile(dstPath, os.O_WRONLY|os.O_CREATE|os.O_EXCL, perm) 75 if err != nil { 76 return err 77 } 78 79 _, err = io.Copy(dst, src) 80 if closeErr := dst.Close(); err == nil { 81 err = closeErr 82 } 83 return err 84 }) 85} 86