-
Notifications
You must be signed in to change notification settings - Fork 17.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
os: File.Seek on Windows errors on offsets like 4G-1, 8G-1, 16G-1 #21681
Comments
Seems like the fix plus a test would be rather simple - would you like to give it a try? See https://golang.org/doc/contribute.html |
@mvdan I can try, but it may take a few days. |
|
Gentle ping, how's it going @gilbertchen and @mvdan? |
I tried the trivial change, like: diff --git a/src/syscall/syscall_windows.go b/src/syscall/syscall_windows.go
index 21d5ecf..e6ceb9c 100644
--- a/src/syscall/syscall_windows.go
+++ b/src/syscall/syscall_windows.go
@@ -9,6 +9,7 @@ package syscall
import (
errorspkg "errors"
"internal/race"
+ "runtime"
"sync"
"unicode/utf16"
"unsafe"
@@ -332,6 +333,16 @@ func Write(fd Handle, p []byte) (n int, err error) {
var ioSync int64
+var procSetFilePointerEx = modkernel32.NewProc("SetFilePointerEx")
+
+func setFilePointerEx(handle Handle, distToMove int64, newFilePointer *int64, whence uint32) error {
+ _, _, e1 := Syscall6(procSetFilePointerEx.Addr(), 4, uintptr(distToMove), uintptr(unsafe.Pointer(newFilePointer)), uintptr(whence), 0, 0, 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,13 +353,17 @@ 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
}
+ if runtime.GOARCH == "amd64" {
+ err = setFilePointerEx(fd, offset, &newoffset, w)
+ return
+ }
+ hi := int32(offset >> 32)
+ lo := int32(offset)
rlo, e := SetFilePointer(fd, lo, &hi, w)
if e != nil {
return 0, e But that doesn't work. Looks like Also, that Maybe @alexbrainman is interested in looking into this? |
This is an existing issue. We can look at a CL, but otherwise kicking it down the road. |
This is the how I think the remarks about overlapped IO and multithreading are irrelevant for the implementation of this function, it's clear that two threads trying to set the file pointer and read a bit from the file must synchronize somehow, that's no difference between |
Yeah, true. I just don't know the calling convention for GOARCH=386 passing a LARGE_INTEGER to Windows via Go's Syscall6. But then again, my change also didn't work on amd64, so not sure what I screwed up. |
I would like to fix this, but I have been busy lately. I will look into this when I have time, unless someone beats to me. Alex |
@bradfitz |
@as, hah. Whoops. Thanks! |
Change https://golang.org/cl/82535 mentions this issue: |
Ah, here's what LARGE_INTEGER means: https://msdn.microsoft.com/en-us/library/windows/desktop/aa383713(v=vs.85).aspx |
And I can repro that my new test fails on 386 and passes with my new fix on amd64:
Fixing on 386 now. |
Please answer these questions before submitting your issue. Thanks!
What version of Go are you using (
go version
)?1.8.1
Does this issue reproduce with the latest release?
I didn't retry, but by looking at the relevant code on the master branch it looks like the bug is still there.
What operating system and processor architecture are you using (
go env
)?Windows 64 bit
What did you do?
Create a 4GB sparse file by moving the file pointer to before the last byte and then write a \x00
Code at:
https://play.golang.org/p/kzMJvpa_eJ
If possible, provide a recipe for reproducing the error.
A complete runnable program is good.
A link on play.golang.org is best.
What did you expect to see?
It should create the file and returns with success
What did you see instead?
Instead it returns an
invalid argument
errorThis is because SetFilePointer did not handle the return value correctly. According to Microsoft's doc:
SetFilePointer in Go always returns the error if you pass 4G - 1, 8G - 1, 16G - 1, etc to the Seek function, even if the operation does return successfully.
The text was updated successfully, but these errors were encountered: