Source file src/cmd/internal/bootstrap_test/overlaydir_test.go

     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  
     5  package bootstrap_test
     6  
     7  import (
     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.
    19  func 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  		if err != nil {
    47  			return err
    48  		}
    49  		perm := info.Mode() & os.ModePerm
    50  		if info.Mode()&os.ModeSymlink != 0 {
    51  			info, err = os.Stat(srcPath)
    52  			if err != nil {
    53  				return err
    54  			}
    55  			perm = info.Mode() & os.ModePerm
    56  		}
    57  
    58  		// Always make copies of directories.
    59  		// If we add a file in the overlay, we don't want to add it in the original.
    60  		if info.IsDir() {
    61  			return os.MkdirAll(dstPath, perm|0200)
    62  		}
    63  
    64  		// If we can use a hard link, do that instead of copying bytes.
    65  		// Go builds don't like symlinks in some cases, such as go:embed.
    66  		if err := os.Link(srcPath, dstPath); err == nil {
    67  			return nil
    68  		}
    69  
    70  		// Otherwise, copy the bytes.
    71  		src, err := os.Open(srcPath)
    72  		if err != nil {
    73  			return err
    74  		}
    75  		defer src.Close()
    76  
    77  		dst, err := os.OpenFile(dstPath, os.O_WRONLY|os.O_CREATE|os.O_EXCL, perm)
    78  		if err != nil {
    79  			return err
    80  		}
    81  
    82  		_, err = io.Copy(dst, src)
    83  		if closeErr := dst.Close(); err == nil {
    84  			err = closeErr
    85  		}
    86  		return err
    87  	})
    88  }
    89  

View as plain text