diff --git a/pkg/fsutil/file.go b/pkg/fsutil/file.go index 7d91679fe..1bf982666 100644 --- a/pkg/fsutil/file.go +++ b/pkg/fsutil/file.go @@ -11,29 +11,55 @@ import ( "github.com/stashapp/stash/pkg/logger" ) +// CopyFile copies the contents of the file at srcpath to a regular file at dstpath. +// It will copy the last modified timestamp +// If dstpath already exists the function will fail. +func CopyFile(srcpath, dstpath string) (err error) { + r, err := os.Open(srcpath) + if err != nil { + return err + } + + w, err := os.OpenFile(dstpath, os.O_CREATE|os.O_EXCL, 0666) + if err != nil { + r.Close() // We need to close the input file as the defer below would not be called. + return err + } + + defer func() { + r.Close() // ok to ignore error: file was opened read-only. + e := w.Close() + // Report the error from w.Close, if any. + // But do so only if there isn't already an outgoing error. + if e != nil && err == nil { + err = e + } + // Copy modified time + if err == nil { + // io.Copy succeeded, we should fix the dstpath timestamp + srcFileInfo, e := os.Stat(srcpath) + if e != nil { + err = e + return + } + + e = os.Chtimes(dstpath, srcFileInfo.ModTime(), srcFileInfo.ModTime()) + if e != nil { + err = e + } + } + }() + + _, err = io.Copy(w, r) + return err +} + // SafeMove attempts to move the file with path src to dest using os.Rename. If this fails, then it copies src to dest, then deletes src. func SafeMove(src, dst string) error { err := os.Rename(src, dst) if err != nil { - in, err := os.Open(src) - if err != nil { - return err - } - defer in.Close() - - out, err := os.Create(dst) - if err != nil { - return err - } - defer out.Close() - - _, err = io.Copy(out, in) - if err != nil { - return err - } - - err = out.Close() + err = CopyFile(src, dst) if err != nil { return err }