]> Cypherpunks repositories - gostls13.git/commitdiff
syscall: make Seek use SetFilePointerEx on Windows, allowing large seek offsets
authorBrad Fitzpatrick <bradfitz@golang.org>
Wed, 6 Dec 2017 18:20:26 +0000 (18:20 +0000)
committerBrad Fitzpatrick <bradfitz@golang.org>
Thu, 7 Dec 2017 19:32:24 +0000 (19:32 +0000)
Fixes #21681
Updates #21728

Change-Id: I79cf4564c1355ecab891102d4215cbbffd8eb0ce
Reviewed-on: https://go-review.googlesource.com/82535
Reviewed-by: Ian Lance Taylor <iant@golang.org>
src/os/os_test.go
src/syscall/syscall_windows.go

index 804cf4a1db29b76ebd6da6828b4a2be43a2e6338..5739dc207f4b509a7eca166b2018d287024e7b32 100644 (file)
@@ -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)
                }
index 21d5ecfcb35fdd62793d5ba2d53876651a26419b..9026fcdacf959198f84b61a5ee4e79cf66025953 100644 (file)
@@ -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) {