1
0
mirror of https://github.com/golang/go synced 2024-11-13 16:50:23 -07:00

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 <bradfitz@golang.org>
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
This commit is contained in:
Daniel Theophanes 2015-02-26 12:10:11 -08:00 committed by Brad Fitzpatrick
parent ef49b4ca78
commit 92c57363e0
7 changed files with 102 additions and 59 deletions

View File

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

View File

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

View File

@ -1,47 +0,0 @@
// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package os_test
import (
"io/ioutil"
"os"
"path/filepath"
"testing"
)
func TestErrIsExistAfterRename(t *testing.T) {
dir, err := ioutil.TempDir("", "go-build")
if err != nil {
t.Fatalf("Create temp directory: %v", err)
}
defer os.RemoveAll(dir)
src := filepath.Join(dir, "src")
dest := filepath.Join(dir, "dest")
f, err := os.Create(src)
if err != nil {
t.Fatalf("Create file %v: %v", src, err)
}
f.Close()
err = os.Rename(src, dest)
if err != nil {
t.Fatalf("Rename %v to %v: %v", src, dest, err)
}
f, err = os.Create(src)
if err != nil {
t.Fatalf("Create file %v: %v", src, err)
}
f.Close()
err = os.Rename(src, dest)
if err == nil {
t.Fatal("Rename should have failed")
}
if s := checkErrorPredicate("os.IsExist", os.IsExist, err); s != "" {
t.Fatal(s)
return
}
}

View File

@ -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())

View File

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

View File

@ -5,6 +5,7 @@
package os
import (
"internal/syscall/windows"
"io"
"runtime"
"sync"
@ -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) {

View File

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