github.com/paketo-buildpacks/packit@v1.3.2-0.20211206231111-86b75c657449/fs/copy.go (about) 1 package fs 2 3 import ( 4 "errors" 5 "fmt" 6 "io" 7 "os" 8 "path/filepath" 9 ) 10 11 // Copy will move a source file or directory to a destination. For directories, 12 // move will remap relative symlinks ensuring that they align with the 13 // destination directory. If the destination exists prior to invocation, it 14 // will be removed. 15 func Copy(source, destination string) error { 16 err := os.Remove(destination) 17 if err != nil { 18 if !errors.Is(err, os.ErrNotExist) { 19 return fmt.Errorf("failed to copy: destination exists: %w", err) 20 } 21 } 22 23 info, err := os.Stat(source) 24 if err != nil { 25 return err 26 } 27 28 if info.IsDir() { 29 err = copyDirectory(source, destination) 30 if err != nil { 31 return err 32 } 33 } else { 34 err = copyFile(source, destination) 35 if err != nil { 36 return err 37 } 38 } 39 40 return nil 41 } 42 43 func copyFile(source, destination string) error { 44 sourceFile, err := os.Open(source) 45 if err != nil { 46 return err 47 } 48 defer sourceFile.Close() 49 50 destinationFile, err := os.Create(destination) 51 if err != nil { 52 return err 53 } 54 defer destinationFile.Close() 55 56 _, err = io.Copy(destinationFile, sourceFile) 57 if err != nil { 58 return err 59 } 60 61 info, err := sourceFile.Stat() 62 if err != nil { 63 return err 64 } 65 66 err = os.Chmod(destination, info.Mode()) 67 if err != nil { 68 return err 69 } 70 71 return nil 72 } 73 74 func copyDirectory(source, destination string) error { 75 err := filepath.Walk(source, func(path string, info os.FileInfo, err error) error { 76 if err != nil { 77 return err 78 } 79 80 path, err = filepath.Rel(source, path) 81 if err != nil { 82 return err 83 } 84 85 switch { 86 case info.IsDir(): 87 err = os.Mkdir(filepath.Join(destination, path), os.ModePerm) 88 if err != nil { 89 return err 90 } 91 92 case (info.Mode() & os.ModeSymlink) != 0: 93 err = copyLink(source, destination, path) 94 if err != nil { 95 return err 96 } 97 98 default: 99 err = copyFile(filepath.Join(source, path), filepath.Join(destination, path)) 100 if err != nil { 101 return err 102 } 103 } 104 105 return nil 106 }) 107 108 if err != nil { 109 return err 110 } 111 112 return nil 113 } 114 115 func copyLink(source, destination, path string) error { 116 link, err := os.Readlink(filepath.Join(source, path)) 117 if err != nil { 118 return err 119 } 120 121 err = os.Symlink(link, filepath.Join(destination, path)) 122 if err != nil { 123 return err 124 } 125 126 return nil 127 }