Skip to content

Commit 92c5736

Browse files
kardianosbradfitz
authored andcommitted
os: windows Rename should overwrite destination file.
Rename now uses MoveFileEx which was previously not available to use because it is not supported on Windows 2000. Change-Id: I583d029c4467c9be6d1574a790c423559b441e87 Reviewed-on: https://go-review.googlesource.com/6140 Reviewed-by: Brad Fitzpatrick <[email protected]> Run-TryBot: Brad Fitzpatrick <[email protected]>
1 parent ef49b4c commit 92c5736

File tree

7 files changed

+102
-59
lines changed

7 files changed

+102
-59
lines changed

src/internal/syscall/windows/syscall_windows.go

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,8 +95,8 @@ const (
9595
)
9696

9797
//sys GetAdaptersAddresses(family uint32, flags uint32, reserved uintptr, adapterAddresses *IpAdapterAddresses, sizeOfPointer *uint32) (errcode error) = iphlpapi.GetAdaptersAddresses
98-
9998
//sys GetComputerNameEx(nameformat uint32, buf *uint16, n *uint32) (err error) = GetComputerNameExW
99+
//sys MoveFileEx(from *uint16, to *uint16, flags uint32) (err error) = MoveFileExW
100100

101101
const (
102102
ComputerNameNetBIOS = 0
@@ -108,4 +108,23 @@ const (
108108
ComputerNamePhysicalDnsDomain = 6
109109
ComputerNamePhysicalDnsFullyQualified = 7
110110
ComputerNameMax = 8
111+
112+
MOVEFILE_REPLACE_EXISTING = 0x1
113+
MOVEFILE_COPY_ALLOWED = 0x2
114+
MOVEFILE_DELAY_UNTIL_REBOOT = 0x4
115+
MOVEFILE_WRITE_THROUGH = 0x8
116+
MOVEFILE_CREATE_HARDLINK = 0x10
117+
MOVEFILE_FAIL_IF_NOT_TRACKABLE = 0x20
111118
)
119+
120+
func Rename(oldpath, newpath string) error {
121+
from, err := syscall.UTF16PtrFromString(oldpath)
122+
if err != nil {
123+
return err
124+
}
125+
to, err := syscall.UTF16PtrFromString(newpath)
126+
if err != nil {
127+
return err
128+
}
129+
return MoveFileEx(from, to, MOVEFILE_REPLACE_EXISTING)
130+
}

src/internal/syscall/windows/zsyscall_windows.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ var (
1313

1414
procGetAdaptersAddresses = modiphlpapi.NewProc("GetAdaptersAddresses")
1515
procGetComputerNameExW = modkernel32.NewProc("GetComputerNameExW")
16+
procMoveFileExW = modkernel32.NewProc("MoveFileExW")
1617
)
1718

1819
func GetAdaptersAddresses(family uint32, flags uint32, reserved uintptr, adapterAddresses *IpAdapterAddresses, sizeOfPointer *uint32) (errcode error) {
@@ -34,3 +35,15 @@ func GetComputerNameEx(nameformat uint32, buf *uint16, n *uint32) (err error) {
3435
}
3536
return
3637
}
38+
39+
func MoveFileEx(from *uint16, to *uint16, flags uint32) (err error) {
40+
r1, _, e1 := syscall.Syscall(procMoveFileExW.Addr(), 3, uintptr(unsafe.Pointer(from)), uintptr(unsafe.Pointer(to)), uintptr(flags))
41+
if r1 == 0 {
42+
if e1 != 0 {
43+
err = error(e1)
44+
} else {
45+
err = syscall.EINVAL
46+
}
47+
}
48+
return
49+
}

src/os/error_windows_test.go

Lines changed: 0 additions & 47 deletions
This file was deleted.

src/os/file_posix.go

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -28,14 +28,6 @@ func Readlink(name string) (string, error) {
2828
}
2929
}
3030

31-
func rename(oldname, newname string) error {
32-
e := syscall.Rename(oldname, newname)
33-
if e != nil {
34-
return &LinkError{"rename", oldname, newname, e}
35-
}
36-
return nil
37-
}
38-
3931
// syscallMode returns the syscall-specific mode bits from Go's portable mode bits.
4032
func syscallMode(i FileMode) (o uint32) {
4133
o |= uint32(i.Perm())

src/os/file_unix.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,14 @@ import (
1212
"syscall"
1313
)
1414

15+
func rename(oldname, newname string) error {
16+
e := syscall.Rename(oldname, newname)
17+
if e != nil {
18+
return &LinkError{"rename", oldname, newname, e}
19+
}
20+
return nil
21+
}
22+
1523
// File represents an open file descriptor.
1624
type File struct {
1725
*file

src/os/file_windows.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
package os
66

77
import (
8+
"internal/syscall/windows"
89
"io"
910
"runtime"
1011
"sync"
@@ -460,6 +461,14 @@ func Remove(name string) error {
460461
return &PathError{"remove", name, e}
461462
}
462463

464+
func rename(oldname, newname string) error {
465+
e := windows.Rename(oldname, newname)
466+
if e != nil {
467+
return &LinkError{"rename", oldname, newname, e}
468+
}
469+
return nil
470+
}
471+
463472
// Pipe returns a connected pair of Files; reads from r return bytes written to w.
464473
// It returns the files and an error, if any.
465474
func Pipe() (r *File, w *File, err error) {

src/os/os_test.go

Lines changed: 52 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -708,13 +708,16 @@ func TestRename(t *testing.T) {
708708
defer chtmpdir(t)()
709709
}
710710
from, to := "renamefrom", "renameto"
711-
Remove(to) // Just in case.
711+
// Ensure we are not testing the overwrite case here.
712+
Remove(from)
713+
Remove(to)
714+
712715
file, err := Create(from)
713716
if err != nil {
714-
t.Fatalf("open %q failed: %v", to, err)
717+
t.Fatalf("open %q failed: %v", from, err)
715718
}
716719
if err = file.Close(); err != nil {
717-
t.Errorf("close %q failed: %v", to, err)
720+
t.Errorf("close %q failed: %v", from, err)
718721
}
719722
err = Rename(from, to)
720723
if err != nil {
@@ -727,6 +730,52 @@ func TestRename(t *testing.T) {
727730
}
728731
}
729732

733+
func TestRenameOverwriteDest(t *testing.T) {
734+
if runtime.GOOS == "plan9" {
735+
t.Skip("skipping on plan9")
736+
}
737+
if runtime.GOOS == "darwin" && runtime.GOARCH == "arm" {
738+
defer chtmpdir(t)()
739+
}
740+
from, to := "renamefrom", "renameto"
741+
// Just in case.
742+
Remove(from)
743+
Remove(to)
744+
745+
toData := []byte("to")
746+
fromData := []byte("from")
747+
748+
err := ioutil.WriteFile(to, toData, 0777)
749+
if err != nil {
750+
t.Fatalf("write file %q failed: %v", to, err)
751+
}
752+
753+
err = ioutil.WriteFile(from, fromData, 0777)
754+
if err != nil {
755+
t.Fatalf("write file %q failed: %v", from, err)
756+
}
757+
err = Rename(from, to)
758+
if err != nil {
759+
t.Fatalf("rename %q, %q failed: %v", to, from, err)
760+
}
761+
defer Remove(to)
762+
763+
_, err = Stat(from)
764+
if err == nil {
765+
t.Errorf("from file %q still exists", from)
766+
}
767+
if err != nil && !IsNotExist(err) {
768+
t.Fatalf("stat from: %v", err)
769+
}
770+
toFi, err := Stat(to)
771+
if err != nil {
772+
t.Fatalf("stat %q failed: %v", to, err)
773+
}
774+
if toFi.Size() != int64(len(fromData)) {
775+
t.Errorf(`"to" size = %d; want %d (old "from" size)`, toFi.Size(), len(fromData))
776+
}
777+
}
778+
730779
func exec(t *testing.T, dir, cmd string, args []string, expect string) {
731780
r, w, err := Pipe()
732781
if err != nil {

0 commit comments

Comments
 (0)