github.com/360nenz/gu@v0.0.0-20230623171552-8f1e25eda00d/gulang/misc/cgo/stdio/overlaydir_test.go (about)

     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 stdio_test
     6  
     7  import (
     8  	"io"
     9  	"os"
    10  	"path/filepath"
    11  	"strings"
    12  )
    13  
    14  // overlayDir makes a minimal-overhead copy of srcRoot in which new files may be added.
    15  //
    16  // TODO: Once we no longer need to support the misc module in GOPATH mode,
    17  // factor this function out into a package to reduce duplication.
    18  func overlayDir(dstRoot, srcRoot string) error {
    19  	dstRoot = filepath.Clean(dstRoot)
    20  	if err := os.MkdirAll(dstRoot, 0777); err != nil {
    21  		return err
    22  	}
    23  
    24  	srcRoot, err := filepath.Abs(srcRoot)
    25  	if err != nil {
    26  		return err
    27  	}
    28  
    29  	return filepath.Walk(srcRoot, func(srcPath string, info os.FileInfo, err error) error {
    30  		if err != nil || srcPath == srcRoot {
    31  			return err
    32  		}
    33  
    34  		suffix := strings.TrimPrefix(srcPath, srcRoot)
    35  		for len(suffix) > 0 && suffix[0] == filepath.Separator {
    36  			suffix = suffix[1:]
    37  		}
    38  		dstPath := filepath.Join(dstRoot, suffix)
    39  
    40  		perm := info.Mode() & os.ModePerm
    41  		if info.Mode()&os.ModeSymlink != 0 {
    42  			info, err = os.Stat(srcPath)
    43  			if err != nil {
    44  				return err
    45  			}
    46  			perm = info.Mode() & os.ModePerm
    47  		}
    48  
    49  		// Always copy directories (don't symlink them).
    50  		// If we add a file in the overlay, we don't want to add it in the original.
    51  		if info.IsDir() {
    52  			return os.MkdirAll(dstPath, perm|0200)
    53  		}
    54  
    55  		// If the OS supports symlinks, use them instead of copying bytes.
    56  		if err := os.Symlink(srcPath, dstPath); err == nil {
    57  			return nil
    58  		}
    59  
    60  		// Otherwise, copy the bytes.
    61  		src, err := os.Open(srcPath)
    62  		if err != nil {
    63  			return err
    64  		}
    65  		defer src.Close()
    66  
    67  		dst, err := os.OpenFile(dstPath, os.O_WRONLY|os.O_CREATE|os.O_EXCL, perm)
    68  		if err != nil {
    69  			return err
    70  		}
    71  
    72  		_, err = io.Copy(dst, src)
    73  		if closeErr := dst.Close(); err == nil {
    74  			err = closeErr
    75  		}
    76  		return err
    77  	})
    78  }