From 7c46b62d0a6e2353db68da963c390b094e359a92 Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Wed, 6 Dec 2017 18:20:26 +0000 Subject: [PATCH] syscall: make Seek use SetFilePointerEx on Windows, allowing large seek offsets Fixes #21681 Updates #21728 Change-Id: I79cf4564c1355ecab891102d4215cbbffd8eb0ce Reviewed-on: https://go-review.googlesource.com/82535 Reviewed-by: Ian Lance Taylor --- src/os/os_test.go | 20 ++++++++++++++++---- src/syscall/syscall_windows.go | 30 +++++++++++++++++++++++------- 2 files changed, 39 insertions(+), 11 deletions(-) diff --git a/src/os/os_test.go b/src/os/os_test.go index 804cf4a1db2..5739dc207f4 100644 --- a/src/os/os_test.go +++ b/src/os/os_test.go @@ -1362,14 +1362,26 @@ func TestSeek(t *testing.T) { {-1, io.SeekEnd, int64(len(data)) - 1}, {1 << 33, io.SeekStart, 1 << 33}, {1 << 33, io.SeekEnd, 1<<33 + int64(len(data))}, + + // Issue 21681, Windows 4G-1, etc: + {1<<32 - 1, io.SeekStart, 1<<32 - 1}, + {0, io.SeekCurrent, 1<<32 - 1}, + {2<<32 - 1, io.SeekStart, 2<<32 - 1}, + {0, io.SeekCurrent, 2<<32 - 1}, } for i, tt := range tests { + if runtime.GOOS == "nacl" && tt.out > 1<<30 { + t.Logf("skipping test case #%d on nacl; https://golang.org/issue/21728", i) + continue + } off, err := f.Seek(tt.in, tt.whence) if off != tt.out || err != nil { - if e, ok := err.(*PathError); ok && e.Err == syscall.EINVAL && tt.out > 1<<32 { - // Reiserfs rejects the big seeks. - // https://golang.org/issue/91 - break + if e, ok := err.(*PathError); ok && e.Err == syscall.EINVAL && tt.out > 1<<32 && runtime.GOOS == "linux" { + mounts, _ := ioutil.ReadFile("/proc/mounts") + if strings.Contains(string(mounts), "reiserfs") { + // Reiserfs rejects the big seeks. + t.Skipf("skipping test known to fail on reiserfs; https://golang.org/issue/91") + } } t.Errorf("#%d: Seek(%v, %v) = %v, %v want %v, nil", i, tt.in, tt.whence, off, err, tt.out) } diff --git a/src/syscall/syscall_windows.go b/src/syscall/syscall_windows.go index 21d5ecfcb35..9026fcdacf9 100644 --- a/src/syscall/syscall_windows.go +++ b/src/syscall/syscall_windows.go @@ -332,6 +332,27 @@ func Write(fd Handle, p []byte) (n int, err error) { var ioSync int64 +var procSetFilePointerEx = modkernel32.NewProc("SetFilePointerEx") + +const ptrSize = unsafe.Sizeof(uintptr(0)) + +// setFilePointerEx calls SetFilePointerEx. +// See https://msdn.microsoft.com/en-us/library/windows/desktop/aa365542(v=vs.85).aspx +func setFilePointerEx(handle Handle, distToMove int64, newFilePointer *int64, whence uint32) error { + var e1 Errno + if ptrSize == 8 { + _, _, e1 = Syscall6(procSetFilePointerEx.Addr(), 4, uintptr(handle), uintptr(distToMove), uintptr(unsafe.Pointer(newFilePointer)), uintptr(whence), 0, 0) + } else { + // distToMove is a LARGE_INTEGER: + // https://msdn.microsoft.com/en-us/library/windows/desktop/aa383713(v=vs.85).aspx + _, _, e1 = Syscall6(procSetFilePointerEx.Addr(), 5, uintptr(handle), uintptr(distToMove), uintptr(distToMove>>32), uintptr(unsafe.Pointer(newFilePointer)), uintptr(whence), 0) + } + if e1 != 0 { + return errnoErr(e1) + } + return nil +} + func Seek(fd Handle, offset int64, whence int) (newoffset int64, err error) { var w uint32 switch whence { @@ -342,18 +363,13 @@ func Seek(fd Handle, offset int64, whence int) (newoffset int64, err error) { case 2: w = FILE_END } - hi := int32(offset >> 32) - lo := int32(offset) // use GetFileType to check pipe, pipe can't do seek ft, _ := GetFileType(fd) if ft == FILE_TYPE_PIPE { return 0, ESPIPE } - rlo, e := SetFilePointer(fd, lo, &hi, w) - if e != nil { - return 0, e - } - return int64(hi)<<32 + int64(rlo), nil + err = setFilePointerEx(fd, offset, &newoffset, w) + return } func Close(fd Handle) (err error) {