diff --git a/disksync.go b/disksync.go index 2641028..4cc3ef9 100644 --- a/disksync.go +++ b/disksync.go @@ -149,7 +149,7 @@ func (a *Account) writeDirtyToDisk() error { // TODO(jrick): this should be atomic on *nix, but is not on // Windows. Use _windows.go to provide atomic renames. - if err = os.Rename(tmpfilepath, utxofilepath); err != nil { + if err = Rename(tmpfilepath, utxofilepath); err != nil { return err } @@ -175,7 +175,7 @@ func (a *Account) writeDirtyToDisk() error { // TODO(jrick): this should be atomic on *nix, but is not on // Windows. Use _windows.go to provide atomic renames. - if err = os.Rename(tmpfilepath, txfilepath); err != nil { + if err = Rename(tmpfilepath, txfilepath); err != nil { return err } @@ -201,7 +201,7 @@ func (a *Account) writeDirtyToDisk() error { // TODO(jrick): this should be atomic on *nix, but is not on // Windows. Use _windows.go to provide atomic renames. - if err = os.Rename(tmpfilepath, wfilepath); err != nil { + if err = Rename(tmpfilepath, wfilepath); err != nil { return err } diff --git a/rename_plan9.go b/rename_plan9.go new file mode 100644 index 0000000..a0992d6 --- /dev/null +++ b/rename_plan9.go @@ -0,0 +1,47 @@ +// The following is adapted from goleveldb +// (https://github.com/syndtr/goleveldb) under the following license: +// +// Copyright 2012 Suryandaru Triandana +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package main + +import ( + "os" + "path/filepath" +) + +// Rename provides an atomic file rename. newpath is replaced if it +// already exists. +func Rename(oldpath, newpath string) error { + if _, err := os.Stat(newpath); err == nil { + if err := os.Remove(newpath); err != nil { + return err + } + } + + _, fname := filepath.Split(newpath) + return os.Rename(oldpath, fname) +} diff --git a/rename_unix.go b/rename_unix.go new file mode 100644 index 0000000..e6b581c --- /dev/null +++ b/rename_unix.go @@ -0,0 +1,27 @@ +// Copyright (c) 2013 Conformal Systems LLC +// +// Permission to use, copy, modify, and distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +// +build !windows,!plan9 + +package main + +import ( + "os" +) + +// Rename provides an atomic file rename. newpath is replaced if it +// already exists. +func Rename(oldpath, newpath string) error { + return os.Rename(oldpath, newpath) +} diff --git a/rename_windows.go b/rename_windows.go new file mode 100644 index 0000000..a976de4 --- /dev/null +++ b/rename_windows.go @@ -0,0 +1,71 @@ +// The following is adapted from goleveldb +// (https://github.com/syndtr/goleveldb) under the following license: +// +// Copyright 2012 Suryandaru Triandana +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package main + +import ( + "syscall" + "unsafe" +) + +var ( + modkernel32 = syscall.NewLazyDLL("kernel32.dll") + procMoveFileExW = modkernel32.NewProc("MoveFileExW") +) + +const ( + _MOVEFILE_REPLACE_EXISTING = 1 +) + +func moveFileEx(from *uint16, to *uint16, flags uint32) error { + r1, _, e1 := syscall.Syscall(procMoveFileExW.Addr(), 3, + uintptr(unsafe.Pointer(from)), uintptr(unsafe.Pointer(to)), + uintptr(flags)) + if r1 == 0 { + if e1 != 0 { + return error(e1) + } else { + return syscall.EINVAL + } + } + return nil +} + +// Rename provides an atomic file rename. newpath is replaced if it +// already exists. +func Rename(oldpath, newpath string) error { + from, err := syscall.UTF16PtrFromString(oldpath) + if err != nil { + return err + } + to, err := syscall.UTF16PtrFromString(newpath) + if err != nil { + return err + } + return moveFileEx(from, to, _MOVEFILE_REPLACE_EXISTING) +} diff --git a/updates.go b/updates.go index 978b2b6..b9f6b9c 100644 --- a/updates.go +++ b/updates.go @@ -108,7 +108,7 @@ func updateOldFileLocations() { old := filepath.Join(cfg.DataDir, fi[i].Name(), "wallet.bin") if fileExists(old) { new := accountFilename("wallet.bin", account, netdir) - if err := os.Rename(old, new); err != nil { + if err := Rename(old, new); err != nil { log.Errorf("Cannot move old %v for account %v to new location: %v", "wallet.bin", account, err) os.Exit(1) @@ -119,7 +119,7 @@ func updateOldFileLocations() { old = filepath.Join(cfg.DataDir, fi[i].Name(), "tx.bin") if fileExists(old) { new := accountFilename("tx.bin", account, netdir) - if err := os.Rename(old, new); err != nil { + if err := Rename(old, new); err != nil { log.Errorf("Cannot move old %v for account %v to new location: %v", "tx.bin", account, err) os.Exit(1) @@ -130,7 +130,7 @@ func updateOldFileLocations() { old = filepath.Join(cfg.DataDir, fi[i].Name(), "utxo.bin") if fileExists(old) { new := accountFilename("utxo.bin", account, netdir) - if err := os.Rename(old, new); err != nil { + if err := Rename(old, new); err != nil { log.Errorf("Cannot move old %v for account %v to new location: %v", "utxo.bin", account, err) os.Exit(1)