github.com/hashicorp/terraform-plugin-sdk@v1.17.2/internal/configs/configload/copy_dir.go (about)

     1  package configload
     2  
     3  import (
     4  	"io"
     5  	"os"
     6  	"path/filepath"
     7  	"strings"
     8  )
     9  
    10  // copyDir copies the src directory contents into dst. Both directories
    11  // should already exist.
    12  func copyDir(dst, src string) error {
    13  	src, err := filepath.EvalSymlinks(src)
    14  	if err != nil {
    15  		return err
    16  	}
    17  
    18  	walkFn := func(path string, info os.FileInfo, err error) error {
    19  		if err != nil {
    20  			return err
    21  		}
    22  
    23  		if path == src {
    24  			return nil
    25  		}
    26  
    27  		if strings.HasPrefix(filepath.Base(path), ".") {
    28  			// Skip any dot files
    29  			if info.IsDir() {
    30  				return filepath.SkipDir
    31  			} else {
    32  				return nil
    33  			}
    34  		}
    35  
    36  		// The "path" has the src prefixed to it. We need to join our
    37  		// destination with the path without the src on it.
    38  		dstPath := filepath.Join(dst, path[len(src):])
    39  
    40  		// we don't want to try and copy the same file over itself.
    41  		if eq, err := sameFile(path, dstPath); eq {
    42  			return nil
    43  		} else if err != nil {
    44  			return err
    45  		}
    46  
    47  		// If we have a directory, make that subdirectory, then continue
    48  		// the walk.
    49  		if info.IsDir() {
    50  			if path == filepath.Join(src, dst) {
    51  				// dst is in src; don't walk it.
    52  				return nil
    53  			}
    54  
    55  			if err := os.MkdirAll(dstPath, 0755); err != nil {
    56  				return err
    57  			}
    58  
    59  			return nil
    60  		}
    61  
    62  		// If the current path is a symlink, recreate the symlink relative to
    63  		// the dst directory
    64  		if info.Mode()&os.ModeSymlink == os.ModeSymlink {
    65  			target, err := os.Readlink(path)
    66  			if err != nil {
    67  				return err
    68  			}
    69  
    70  			return os.Symlink(target, dstPath)
    71  		}
    72  
    73  		// If we have a file, copy the contents.
    74  		srcF, err := os.Open(path)
    75  		if err != nil {
    76  			return err
    77  		}
    78  		defer srcF.Close()
    79  
    80  		dstF, err := os.Create(dstPath)
    81  		if err != nil {
    82  			return err
    83  		}
    84  		defer dstF.Close()
    85  
    86  		if _, err := io.Copy(dstF, srcF); err != nil {
    87  			return err
    88  		}
    89  
    90  		// Chmod it
    91  		return os.Chmod(dstPath, info.Mode())
    92  	}
    93  
    94  	return filepath.Walk(src, walkFn)
    95  }
    96  
    97  // sameFile tried to determine if to paths are the same file.
    98  // If the paths don't match, we lookup the inode on supported systems.
    99  func sameFile(a, b string) (bool, error) {
   100  	if a == b {
   101  		return true, nil
   102  	}
   103  
   104  	aIno, err := inode(a)
   105  	if err != nil {
   106  		if os.IsNotExist(err) {
   107  			return false, nil
   108  		}
   109  		return false, err
   110  	}
   111  
   112  	bIno, err := inode(b)
   113  	if err != nil {
   114  		if os.IsNotExist(err) {
   115  			return false, nil
   116  		}
   117  		return false, err
   118  	}
   119  
   120  	if aIno > 0 && aIno == bIno {
   121  		return true, nil
   122  	}
   123  
   124  	return false, nil
   125  }