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