github.com/sercand/please@v13.4.0+incompatible/src/fs/copy.go (about)

     1  package fs
     2  
     3  import (
     4  	"os"
     5  	"path"
     6  	"runtime"
     7  )
     8  
     9  // CopyOrLinkFile either copies or hardlinks a file based on the link argument.
    10  // Falls back to a copy if link fails and fallback is true.
    11  func CopyOrLinkFile(from, to string, mode os.FileMode, link, fallback bool) error {
    12  	if link {
    13  		if err := os.Link(from, to); err == nil || !fallback {
    14  			return err
    15  		} else if runtime.GOOS != "linux" && os.IsNotExist(err) {
    16  			// There is an awkward issue on several non-Linux platforms where links to
    17  			// symlinks actually try to link to the target rather than the link itself.
    18  			// In that case we try to recreate a similar symlink at the destination.
    19  			if info, err := os.Lstat(from); err == nil && (info.Mode()&os.ModeSymlink) != 0 {
    20  				dest, err := os.Readlink(from)
    21  				if err != nil {
    22  					return err
    23  				}
    24  				return os.Symlink(dest, to)
    25  			}
    26  			return err
    27  		}
    28  	}
    29  	return CopyFile(from, to, mode)
    30  }
    31  
    32  // RecursiveCopy copies either a single file or a directory.
    33  func RecursiveCopy(from string, to string, mode os.FileMode) error {
    34  	return recursiveCopyOrLinkFile(from, to, mode, false, false)
    35  }
    36  
    37  // RecursiveLink hardlinks either a single file or a directory.
    38  // Note that you can't hardlink directories so the behaviour is much the same as a recursive copy.
    39  // If it can't link then it falls back to a copy.
    40  func RecursiveLink(from string, to string, mode os.FileMode) error {
    41  	return recursiveCopyOrLinkFile(from, to, mode, true, true)
    42  }
    43  
    44  // recursiveCopyOrLinkFile recursively copies or links a file or directory.
    45  // If 'link' is true then we'll hardlink files instead of copying them.
    46  // If 'fallback' is true then we'll fall back to a copy if linking fails.
    47  func recursiveCopyOrLinkFile(from string, to string, mode os.FileMode, link, fallback bool) error {
    48  	if info, err := os.Stat(from); err == nil && info.IsDir() {
    49  		return WalkMode(from, func(name string, isDir bool, fileMode os.FileMode) error {
    50  			dest := path.Join(to, name[len(from):])
    51  			if isDir {
    52  				return os.MkdirAll(dest, DirPermissions)
    53  			}
    54  			return CopyOrLinkFile(name, dest, mode, link, fallback)
    55  		})
    56  	}
    57  	return CopyOrLinkFile(from, to, mode, link, fallback)
    58  }