]> Cypherpunks repositories - gostls13.git/commitdiff
[release-branch.go1.25] internal/poll: don't call Seek for overlapped Windows handles
authorqmuntal <quimmuntal@gmail.com>
Tue, 19 Aug 2025 11:00:02 +0000 (13:00 +0200)
committerCarlos Amedee <carlos@golang.org>
Wed, 1 Oct 2025 15:06:41 +0000 (08:06 -0700)
Overlapped handles don't have the file pointer updated when performing
I/O operations, so there is no need to call FD.Seek to reset the file
pointer.

Also, some overlapped file handles don't support seeking. See #74951.

For #74951.
Fixes #75111.

Change-Id: I0edd53beed7d3862730f3b2ed5fe9ba490e66c06
Reviewed-on: https://go-review.googlesource.com/c/go/+/697295
Reviewed-by: Damien Neil <dneil@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
(cherry picked from commit 509d5f647ffc413bd874c2e2bf6d1b33f9bc0ac2)
Reviewed-on: https://go-review.googlesource.com/c/go/+/704315

src/internal/poll/fd_windows.go
src/os/os_windows_test.go

index 74188c057277ed8e921f3a40c9af8d753cf80633..dd833afd24b0a0fa0caf6176d582399b6bffed04 100644 (file)
@@ -636,12 +636,22 @@ func (fd *FD) Pread(b []byte, off int64) (int, error) {
 
        fd.l.Lock()
        defer fd.l.Unlock()
-       curoffset, err := syscall.Seek(fd.Sysfd, 0, io.SeekCurrent)
-       if err != nil {
-               return 0, err
+       if fd.isBlocking {
+               curoffset, err := syscall.Seek(fd.Sysfd, 0, io.SeekCurrent)
+               if err != nil {
+                       return 0, err
+               }
+               defer syscall.Seek(fd.Sysfd, curoffset, io.SeekStart)
+               defer fd.setOffset(curoffset)
+       } else {
+               // Overlapped handles don't have the file pointer updated
+               // when performing I/O operations, so there is no need to
+               // call Seek to reset the file pointer.
+               // Also, some overlapped file handles don't support seeking.
+               // See https://go.dev/issues/74951.
+               curoffset := fd.offset
+               defer fd.setOffset(curoffset)
        }
-       defer syscall.Seek(fd.Sysfd, curoffset, io.SeekStart)
-       defer fd.setOffset(curoffset)
        o := &fd.rop
        o.InitBuf(b)
        fd.setOffset(off)
@@ -852,12 +862,22 @@ func (fd *FD) Pwrite(buf []byte, off int64) (int, error) {
 
        fd.l.Lock()
        defer fd.l.Unlock()
-       curoffset, err := syscall.Seek(fd.Sysfd, 0, io.SeekCurrent)
-       if err != nil {
-               return 0, err
+       if fd.isBlocking {
+               curoffset, err := syscall.Seek(fd.Sysfd, 0, io.SeekCurrent)
+               if err != nil {
+                       return 0, err
+               }
+               defer syscall.Seek(fd.Sysfd, curoffset, io.SeekStart)
+               defer fd.setOffset(curoffset)
+       } else {
+               // Overlapped handles don't have the file pointer updated
+               // when performing I/O operations, so there is no need to
+               // call Seek to reset the file pointer.
+               // Also, some overlapped file handles don't support seeking.
+               // See https://go.dev/issues/74951.
+               curoffset := fd.offset
+               defer fd.setOffset(curoffset)
        }
-       defer syscall.Seek(fd.Sysfd, curoffset, io.SeekStart)
-       defer fd.setOffset(curoffset)
 
        var ntotal int
        for {
index d9af25d4085c17b94f9ca0e3d7ff28ba3480bcd8..6f091c38184189a063a54eeca36e4202e8b76e2d 100644 (file)
@@ -1883,6 +1883,34 @@ func TestFileOverlappedSeek(t *testing.T) {
        }
 }
 
+func TestFileOverlappedReadAtVolume(t *testing.T) {
+       // Test that we can use File.ReadAt with an overlapped volume handle.
+       // See https://go.dev/issues/74951.
+       t.Parallel()
+       name := `\\.\` + filepath.VolumeName(t.TempDir())
+       namep, err := syscall.UTF16PtrFromString(name)
+       if err != nil {
+               t.Fatal(err)
+       }
+       h, err := syscall.CreateFile(namep,
+               syscall.GENERIC_READ|syscall.GENERIC_WRITE,
+               syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_READ,
+               nil, syscall.OPEN_ALWAYS, syscall.FILE_FLAG_OVERLAPPED, 0)
+       if err != nil {
+               if errors.Is(err, syscall.ERROR_ACCESS_DENIED) {
+                       t.Skip("skipping test: access denied")
+               }
+               t.Fatal(err)
+       }
+       f := os.NewFile(uintptr(h), name)
+       defer f.Close()
+
+       var buf [0]byte
+       if _, err := f.ReadAt(buf[:], 0); err != nil {
+               t.Fatal(err)
+       }
+}
+
 func TestPipe(t *testing.T) {
        t.Parallel()
        r, w, err := os.Pipe()