github.com/grafana/pyroscope@v1.18.0/pkg/util/copy.go (about) 1 /* MIT License 2 * 3 * Copyright (c) 2017 Roland Singer [roland.singer@desertbit.com] 4 * 5 * Permission is hereby granted, free of charge, to any person obtaining a copy 6 * of this software and associated documentation files (the "Software"), to deal 7 * in the Software without restriction, including without limitation the rights 8 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 * copies of the Software, and to permit persons to whom the Software is 10 * furnished to do so, subject to the following conditions: 11 * 12 * The above copyright notice and this permission notice shall be included in all 13 * copies or substantial portions of the Software. 14 * 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 * SOFTWARE. 22 */ 23 24 // CopyFile copies the contents of the file named src to the file named 25 // by dst. The file will be created if it does not already exist. 26 // Attempt to do a symlink first if it fails. The file mode will be copied from the source and 27 // the copied data is synced/flushed to stable storage. 28 // If the destination file exists, all it's contents will be replaced by the contents 29 // of the source file. 30 package util 31 32 import ( 33 "fmt" 34 "io" 35 "io/ioutil" 36 "os" 37 "path/filepath" 38 ) 39 40 func CopyFile(src, dst string) (err error) { 41 // Attempt to create hard link first. 42 if err = os.Link(src, dst); err == nil { 43 return 44 } 45 in, err := os.Open(src) 46 if err != nil { 47 return 48 } 49 defer in.Close() 50 51 out, err := os.Create(dst) 52 if err != nil { 53 return 54 } 55 defer func() { 56 if e := out.Close(); e != nil { 57 err = e 58 } 59 }() 60 61 _, err = io.Copy(out, in) 62 if err != nil { 63 return 64 } 65 66 err = out.Sync() 67 if err != nil { 68 return 69 } 70 71 si, err := os.Stat(src) 72 if err != nil { 73 return 74 } 75 err = os.Chmod(dst, si.Mode()) 76 if err != nil { 77 return 78 } 79 80 return 81 } 82 83 // CopyDir recursively copies a directory tree, attempting to preserve permissions. 84 // Source directory must exist, destination directory must *not* exist. 85 // Symlinks are ignored and skipped. 86 func CopyDir(src string, dst string) (err error) { 87 src = filepath.Clean(src) 88 dst = filepath.Clean(dst) 89 90 si, err := os.Stat(src) 91 if err != nil { 92 return err 93 } 94 if !si.IsDir() { 95 return fmt.Errorf("source is not a directory") 96 } 97 98 _, err = os.Stat(dst) 99 if err != nil && !os.IsNotExist(err) { 100 return 101 } 102 if err == nil { 103 return fmt.Errorf("destination already exists") 104 } 105 106 err = os.MkdirAll(dst, si.Mode()) 107 if err != nil { 108 return 109 } 110 111 entries, err := ioutil.ReadDir(src) 112 if err != nil { 113 return 114 } 115 116 for _, entry := range entries { 117 srcPath := filepath.Join(src, entry.Name()) 118 dstPath := filepath.Join(dst, entry.Name()) 119 120 if entry.IsDir() { 121 err = CopyDir(srcPath, dstPath) 122 if err != nil { 123 return 124 } 125 } else { 126 // Skip symlinks. 127 if entry.Mode()&os.ModeSymlink != 0 { 128 continue 129 } 130 131 err = CopyFile(srcPath, dstPath) 132 if err != nil { 133 return 134 } 135 } 136 } 137 138 return 139 }