Skip to content

Commit

Permalink
os: windows Rename should overwrite destination file.
Browse files Browse the repository at this point in the history
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 <bradfitz@golang.org>
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
  • Loading branch information
kardianos authored and bradfitz committed Apr 9, 2015
1 parent ef49b4c commit 92c5736
Show file tree
Hide file tree
Showing 7 changed files with 102 additions and 59 deletions.
21 changes: 20 additions & 1 deletion src/internal/syscall/windows/syscall_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,8 @@ const (
)

//sys GetAdaptersAddresses(family uint32, flags uint32, reserved uintptr, adapterAddresses *IpAdapterAddresses, sizeOfPointer *uint32) (errcode error) = iphlpapi.GetAdaptersAddresses

//sys GetComputerNameEx(nameformat uint32, buf *uint16, n *uint32) (err error) = GetComputerNameExW
//sys MoveFileEx(from *uint16, to *uint16, flags uint32) (err error) = MoveFileExW

const (
ComputerNameNetBIOS = 0
Expand All @@ -108,4 +108,23 @@ const (
ComputerNamePhysicalDnsDomain = 6
ComputerNamePhysicalDnsFullyQualified = 7
ComputerNameMax = 8

MOVEFILE_REPLACE_EXISTING = 0x1
MOVEFILE_COPY_ALLOWED = 0x2
MOVEFILE_DELAY_UNTIL_REBOOT = 0x4
MOVEFILE_WRITE_THROUGH = 0x8
MOVEFILE_CREATE_HARDLINK = 0x10
MOVEFILE_FAIL_IF_NOT_TRACKABLE = 0x20
)

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)
}
13 changes: 13 additions & 0 deletions src/internal/syscall/windows/zsyscall_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ var (

procGetAdaptersAddresses = modiphlpapi.NewProc("GetAdaptersAddresses")
procGetComputerNameExW = modkernel32.NewProc("GetComputerNameExW")
procMoveFileExW = modkernel32.NewProc("MoveFileExW")
)

func GetAdaptersAddresses(family uint32, flags uint32, reserved uintptr, adapterAddresses *IpAdapterAddresses, sizeOfPointer *uint32) (errcode error) {
Expand All @@ -34,3 +35,15 @@ func GetComputerNameEx(nameformat uint32, buf *uint16, n *uint32) (err error) {
}
return
}

func MoveFileEx(from *uint16, to *uint16, flags uint32) (err error) {
r1, _, e1 := syscall.Syscall(procMoveFileExW.Addr(), 3, uintptr(unsafe.Pointer(from)), uintptr(unsafe.Pointer(to)), uintptr(flags))
if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
err = syscall.EINVAL
}
}
return
}
47 changes: 0 additions & 47 deletions src/os/error_windows_test.go

This file was deleted.

8 changes: 0 additions & 8 deletions src/os/file_posix.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,6 @@ func Readlink(name string) (string, error) {
}
}

func rename(oldname, newname string) error {
e := syscall.Rename(oldname, newname)
if e != nil {
return &LinkError{"rename", oldname, newname, e}
}
return nil
}

// syscallMode returns the syscall-specific mode bits from Go's portable mode bits.
func syscallMode(i FileMode) (o uint32) {
o |= uint32(i.Perm())
Expand Down
8 changes: 8 additions & 0 deletions src/os/file_unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,14 @@ import (
"syscall"
)

func rename(oldname, newname string) error {
e := syscall.Rename(oldname, newname)
if e != nil {
return &LinkError{"rename", oldname, newname, e}
}
return nil
}

// File represents an open file descriptor.
type File struct {
*file
Expand Down
9 changes: 9 additions & 0 deletions src/os/file_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
package os

import (
"internal/syscall/windows"
"io"
"runtime"
"sync"
Expand Down Expand Up @@ -460,6 +461,14 @@ func Remove(name string) error {
return &PathError{"remove", name, e}
}

func rename(oldname, newname string) error {
e := windows.Rename(oldname, newname)
if e != nil {
return &LinkError{"rename", oldname, newname, e}
}
return nil
}

// Pipe returns a connected pair of Files; reads from r return bytes written to w.
// It returns the files and an error, if any.
func Pipe() (r *File, w *File, err error) {
Expand Down
55 changes: 52 additions & 3 deletions src/os/os_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -708,13 +708,16 @@ func TestRename(t *testing.T) {
defer chtmpdir(t)()
}
from, to := "renamefrom", "renameto"
Remove(to) // Just in case.
// Ensure we are not testing the overwrite case here.
Remove(from)
Remove(to)

file, err := Create(from)
if err != nil {
t.Fatalf("open %q failed: %v", to, err)
t.Fatalf("open %q failed: %v", from, err)
}
if err = file.Close(); err != nil {
t.Errorf("close %q failed: %v", to, err)
t.Errorf("close %q failed: %v", from, err)
}
err = Rename(from, to)
if err != nil {
Expand All @@ -727,6 +730,52 @@ func TestRename(t *testing.T) {
}
}

func TestRenameOverwriteDest(t *testing.T) {
if runtime.GOOS == "plan9" {
t.Skip("skipping on plan9")
}
if runtime.GOOS == "darwin" && runtime.GOARCH == "arm" {
defer chtmpdir(t)()
}
from, to := "renamefrom", "renameto"
// Just in case.
Remove(from)
Remove(to)

toData := []byte("to")
fromData := []byte("from")

err := ioutil.WriteFile(to, toData, 0777)
if err != nil {
t.Fatalf("write file %q failed: %v", to, err)
}

err = ioutil.WriteFile(from, fromData, 0777)
if err != nil {
t.Fatalf("write file %q failed: %v", from, err)
}
err = Rename(from, to)
if err != nil {
t.Fatalf("rename %q, %q failed: %v", to, from, err)
}
defer Remove(to)

_, err = Stat(from)
if err == nil {
t.Errorf("from file %q still exists", from)
}
if err != nil && !IsNotExist(err) {
t.Fatalf("stat from: %v", err)
}
toFi, err := Stat(to)
if err != nil {
t.Fatalf("stat %q failed: %v", to, err)
}
if toFi.Size() != int64(len(fromData)) {
t.Errorf(`"to" size = %d; want %d (old "from" size)`, toFi.Size(), len(fromData))
}
}

func exec(t *testing.T, dir, cmd string, args []string, expect string) {
r, w, err := Pipe()
if err != nil {
Expand Down

4 comments on commit 92c5736

@zaz600
Copy link

@zaz600 zaz600 commented on 92c5736 Dec 29, 2015

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

now with MoveFileEx os.Rename("r:\1.txt", "z:\1.txt") on windows generate error:
The system cannot move the file to a different disk drive

@zaz600
Copy link

@zaz600 zaz600 commented on 92c5736 Dec 29, 2015

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When moving a file, the destination can be on a different file system or volume. If the destination is on another drive, you must set the MOVEFILE_COPY_ALLOWED flag in dwFlags.
So if the source

@alexbrainman
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@zaz600
Copy link

@zaz600 zaz600 commented on 92c5736 Dec 29, 2015

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.