Close input file so SafeMove can delete it (#3714)

* Close input file so SafeMove can delete it

This is happening on Windows and over the network but at the end of SafeMove it fails the move with an error that it can't remove the input because it is in use.
It turns out it is in use by the SafeMove itself :)

* Copy the src file mod time
This commit is contained in:
puc9 2023-05-09 18:16:49 -07:00 committed by GitHub
parent e7abeeb4df
commit 61c0098ae6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -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
}