diff --git a/util/fs.go b/util/fs.go index f8f8eb1ee291fb64a02001e3c1828cadcf20bd00..11322e68ac9a139fd970e0e6f5fd016d10ec5e05 100644 --- a/util/fs.go +++ b/util/fs.go @@ -18,6 +18,7 @@ package util import ( "fmt" + "io" "os" ) @@ -43,3 +44,66 @@ func IsDir(directory string) bool { } return fileInfo.IsDir() } + +// copied from http://stackoverflow.com/questions/21060945/simple-way-to-copy-a-file-in-golang +// CopyFile copies a file from src to dst. If src and dst files exist, and are +// the same, then return success. Otherise, attempt to create a hard link +// between the two files. If that fail, copy the file contents from src to dst. +func CopyFile(src, dst string) (err error) { + sfi, err := os.Stat(src) + if err != nil { + return + } + if !sfi.Mode().IsRegular() { + // cannot copy non-regular files (e.g., directories, + // symlinks, devices, etc.) + return fmt.Errorf("CopyFile: non-regular source file %s (%q)", sfi.Name(), sfi.Mode().String()) + } + dfi, err := os.Stat(dst) + if err != nil { + if !os.IsNotExist(err) { + return + } + } else { + if !(dfi.Mode().IsRegular()) { + return fmt.Errorf("CopyFile: non-regular destination file %s (%q)", dfi.Name(), dfi.Mode().String()) + } + if os.SameFile(sfi, dfi) { + return + } + } + // NOTE: [ben] we do not want to create a hard link currently + // if err = os.Link(src, dst); err == nil { + // return + // } + err = copyFileContents(src, dst) + return +} + +// copyFileContents copies the contents of the file named src to the file named +// by dst. The file will be created if it does not already exist. If the +// destination file exists, all its contents will be replaced by the contents +// of the source file. +func copyFileContents(src, dst string) (err error) { + in, err := os.Open(src) + if err != nil { + return + } + defer in.Close() + out, err := os.Create(dst) + if err != nil { + return + } + defer func() { + cerr := out.Close() + if err == nil { + err = cerr + } + }() + if _, err = io.Copy(out, in); err != nil { + return + } + // TODO: [ben] this blocks, so copy should be put in go-routine + err = out.Sync() + return +}