]> Cypherpunks repositories - gostls13.git/commitdiff
[release-branch.go1.25] internal/poll: set the correct file offset in FD.Seek for...
authorqmuntal <quimmuntal@gmail.com>
Tue, 19 Aug 2025 10:10:54 +0000 (12:10 +0200)
committerCherry Mui <cherryyz@google.com>
Mon, 25 Aug 2025 18:19:12 +0000 (11:19 -0700)
Windows doesn't keep the file pointer for overlapped file handles. To
work around this, we keep track of the current offset ourselves and use
it on every Read/Write operation.

When the user calls File.Seek with whence == io.SeekCurrent, it expects
that the offset we keep track of is also accounted for, else the the
seek'ed value won't match the file pointer seen by the user.

Fixes #75083.

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

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

index acc2ab0c6e1eaf471f8aee67f5fe6eee8d1d0c68..74188c057277ed8e921f3a40c9af8d753cf80633 100644 (file)
@@ -1106,6 +1106,12 @@ func (fd *FD) Seek(offset int64, whence int) (int64, error) {
        fd.l.Lock()
        defer fd.l.Unlock()
 
+       if !fd.isBlocking && whence == io.SeekCurrent {
+               // Windows doesn't keep the file pointer for overlapped file handles.
+               // We do it ourselves in case to account for any read or write
+               // operations that may have occurred.
+               offset += fd.offset
+       }
        n, err := syscall.Seek(fd.Sysfd, offset, whence)
        fd.setOffset(n)
        return n, err
index 515d1c135901e0114119d3563fe2a08035340d26..d9af25d4085c17b94f9ca0e3d7ff28ba3480bcd8 100644 (file)
@@ -1845,6 +1845,44 @@ func TestFile(t *testing.T) {
        }
 }
 
+func TestFileOverlappedSeek(t *testing.T) {
+       t.Parallel()
+       name := filepath.Join(t.TempDir(), "foo")
+       f := newFileOverlapped(t, name, true)
+       content := []byte("foo")
+       if _, err := f.Write(content); err != nil {
+               t.Fatal(err)
+       }
+       // Check that the file pointer is at the expected offset.
+       n, err := f.Seek(0, io.SeekCurrent)
+       if err != nil {
+               t.Fatal(err)
+       }
+       if n != int64(len(content)) {
+               t.Errorf("expected file pointer to be at offset %d, got %d", len(content), n)
+       }
+       // Set the file pointer to the start of the file.
+       if _, err := f.Seek(0, io.SeekStart); err != nil {
+               t.Fatal(err)
+       }
+       // Read the first byte.
+       var buf [1]byte
+       if _, err := f.Read(buf[:]); err != nil {
+               t.Fatal(err)
+       }
+       if !bytes.Equal(buf[:], content[:len(buf)]) {
+               t.Errorf("expected %q, got %q", content[:len(buf)], buf[:])
+       }
+       // Check that the file pointer is at the expected offset.
+       n, err = f.Seek(0, io.SeekCurrent)
+       if err != nil {
+               t.Fatal(err)
+       }
+       if n != int64(len(buf)) {
+               t.Errorf("expected file pointer to be at offset %d, got %d", len(buf), n)
+       }
+}
+
 func TestPipe(t *testing.T) {
        t.Parallel()
        r, w, err := os.Pipe()