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  		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  

View as plain text