github.com/hashicorp/terraform-plugin-sdk@v1.17.2/internal/initwd/copy_dir.go (about) 1 package initwd 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 }