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  }