52 lines
1.4 KiB
Go
52 lines
1.4 KiB
Go
// +build plan9 windows,!go1.5
|
|
|
|
// os.Rename on Windows before Go 1.5 and Plan 9 will not overwrite existing
|
|
// files, thus we cannot guarantee atomic saving of file by doing rename.
|
|
// We will have to do some voodoo to minimize data loss on those systems.
|
|
|
|
package safefile
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
)
|
|
|
|
func rename(oldname, newname string) error {
|
|
err := os.Rename(oldname, newname)
|
|
if err != nil {
|
|
// If newname exists ("original"), we will try renaming it to a
|
|
// new temporary name, then renaming oldname to the newname,
|
|
// and deleting the renamed original. If system crashes between
|
|
// renaming and deleting, the original file will still be available
|
|
// under the temporary name, so users can manually recover data.
|
|
// (No automatic recovery is possible because after crash the
|
|
// temporary name is not known.)
|
|
var origtmp string
|
|
for {
|
|
origtmp, err = makeTempName(newname, filepath.Base(newname))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
_, err = os.Stat(origtmp)
|
|
if err == nil {
|
|
continue // most likely will never happen
|
|
}
|
|
break
|
|
}
|
|
err = os.Rename(newname, origtmp)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = os.Rename(oldname, newname)
|
|
if err != nil {
|
|
// Rename still fails, try to revert original rename,
|
|
// ignoring errors.
|
|
os.Rename(origtmp, newname)
|
|
return err
|
|
}
|
|
// Rename succeeded, now delete original file.
|
|
os.Remove(origtmp)
|
|
}
|
|
return nil
|
|
}
|