From: qmuntal Date: Thu, 30 Oct 2025 11:36:42 +0000 (+0100) Subject: os: ignore O_TRUNC errors on named pipes and terminal devices on Windows X-Git-Tag: go1.26rc1~368 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=9f3a108ee0d2f08b4994c4201e54e5a53acbc216;p=gostls13.git os: ignore O_TRUNC errors on named pipes and terminal devices on Windows Prior to Go 1.24, os.OpenFile used to support O_TRUNC on named pipes and terminal devices, even when the truncation was really ignored. This behavior was consistent with Unix semantics. CL 618836 changed the implementation of os.OpenFile on Windows and unintentionally started returning an error when O_TRUNC was used on such files. Fixes #76071 Change-Id: Id10d3d8120ae9aa0548ef05423a172ff4e502ff9 Reviewed-on: https://go-review.googlesource.com/c/go/+/716420 Reviewed-by: Michael Knyszek Reviewed-by: Alex Brainman LUCI-TryBot-Result: Go LUCI Reviewed-by: Damien Neil Reviewed-by: Michael Pratt --- diff --git a/src/internal/syscall/windows/at_windows.go b/src/internal/syscall/windows/at_windows.go index 2890e1fdcf..b7ca8433c2 100644 --- a/src/internal/syscall/windows/at_windows.go +++ b/src/internal/syscall/windows/at_windows.go @@ -131,6 +131,14 @@ func Openat(dirfd syscall.Handle, name string, flag uint64, perm uint32) (_ sysc if flag&syscall.O_TRUNC != 0 { err = syscall.Ftruncate(h, 0) + if err == ERROR_INVALID_PARAMETER { + // ERROR_INVALID_PARAMETER means truncation is not supported on this file handle. + // Unix's O_TRUNC specification says to ignore O_TRUNC on named pipes and terminal devices. + // We do the same here. + if t, err1 := syscall.GetFileType(h); err1 == nil && (t == syscall.FILE_TYPE_PIPE || t == syscall.FILE_TYPE_CHAR) { + err = nil + } + } if err != nil { syscall.CloseHandle(h) return syscall.InvalidHandle, err diff --git a/src/os/os_windows_test.go b/src/os/os_windows_test.go index cd2413d26d..3e7bddc791 100644 --- a/src/os/os_windows_test.go +++ b/src/os/os_windows_test.go @@ -2275,3 +2275,16 @@ func TestOpenFileFlagInvalid(t *testing.T) { } f.Close() } + +func TestOpenFileTruncateNamedPipe(t *testing.T) { + t.Parallel() + name := pipeName() + pipe := newBytePipe(t, name, false) + defer pipe.Close() + + f, err := os.OpenFile(name, os.O_TRUNC|os.O_RDWR|os.O_CREATE, 0666) + if err != nil { + t.Fatal(err) + } + f.Close() +} diff --git a/src/os/root_windows_test.go b/src/os/root_windows_test.go index 8ae6f0c9d3..47643f98d1 100644 --- a/src/os/root_windows_test.go +++ b/src/os/root_windows_test.go @@ -228,3 +228,22 @@ func TestRootSymlinkToDirectory(t *testing.T) { }) } } + +func TestRootOpenFileTruncateNamedPipe(t *testing.T) { + t.Parallel() + name := pipeName() + pipe := newBytePipe(t, name, false) + defer pipe.Close() + + root, err := os.OpenRoot(filepath.Dir(name)) + if err != nil { + t.Fatal(err) + } + defer root.Close() + + f, err := root.OpenFile(filepath.Base(name), os.O_TRUNC|os.O_RDWR|os.O_CREATE, 0666) + if err != nil { + t.Fatal(err) + } + f.Close() +} diff --git a/src/syscall/syscall_windows.go b/src/syscall/syscall_windows.go index 817eeb6811..3e63897b6b 100644 --- a/src/syscall/syscall_windows.go +++ b/src/syscall/syscall_windows.go @@ -468,6 +468,14 @@ func Open(name string, flag int, perm uint32) (fd Handle, err error) { if flag&O_TRUNC == O_TRUNC && (createmode == OPEN_EXISTING || (createmode == OPEN_ALWAYS && err == ERROR_ALREADY_EXISTS)) { err = Ftruncate(h, 0) + if err == _ERROR_INVALID_PARAMETER { + // ERROR_INVALID_PARAMETER means truncation is not supported on this file handle. + // Unix's O_TRUNC specification says to ignore O_TRUNC on named pipes and terminal devices. + // We do the same here. + if t, err1 := GetFileType(h); err1 == nil && (t == FILE_TYPE_PIPE || t == FILE_TYPE_CHAR) { + err = nil + } + } if err != nil { CloseHandle(h) return InvalidHandle, err diff --git a/src/syscall/types_windows.go b/src/syscall/types_windows.go index b40b455e7d..3c6d18a850 100644 --- a/src/syscall/types_windows.go +++ b/src/syscall/types_windows.go @@ -34,6 +34,10 @@ const ( WSAECONNRESET Errno = 10054 ) +const ( + _ERROR_INVALID_PARAMETER Errno = 87 +) + const ( // Invented values to support what package os expects. O_RDONLY = 0x00000