Skip to content

Commit 9f3a108

Browse files
committed
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 <mknyszek@google.com> Reviewed-by: Alex Brainman <alex.brainman@gmail.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Damien Neil <dneil@google.com> Reviewed-by: Michael Pratt <mpratt@google.com>
1 parent 0e1bd8b commit 9f3a108

File tree

5 files changed

+52
-0
lines changed

5 files changed

+52
-0
lines changed

src/internal/syscall/windows/at_windows.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,14 @@ func Openat(dirfd syscall.Handle, name string, flag uint64, perm uint32) (_ sysc
131131

132132
if flag&syscall.O_TRUNC != 0 {
133133
err = syscall.Ftruncate(h, 0)
134+
if err == ERROR_INVALID_PARAMETER {
135+
// ERROR_INVALID_PARAMETER means truncation is not supported on this file handle.
136+
// Unix's O_TRUNC specification says to ignore O_TRUNC on named pipes and terminal devices.
137+
// We do the same here.
138+
if t, err1 := syscall.GetFileType(h); err1 == nil && (t == syscall.FILE_TYPE_PIPE || t == syscall.FILE_TYPE_CHAR) {
139+
err = nil
140+
}
141+
}
134142
if err != nil {
135143
syscall.CloseHandle(h)
136144
return syscall.InvalidHandle, err

src/os/os_windows_test.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2275,3 +2275,16 @@ func TestOpenFileFlagInvalid(t *testing.T) {
22752275
}
22762276
f.Close()
22772277
}
2278+
2279+
func TestOpenFileTruncateNamedPipe(t *testing.T) {
2280+
t.Parallel()
2281+
name := pipeName()
2282+
pipe := newBytePipe(t, name, false)
2283+
defer pipe.Close()
2284+
2285+
f, err := os.OpenFile(name, os.O_TRUNC|os.O_RDWR|os.O_CREATE, 0666)
2286+
if err != nil {
2287+
t.Fatal(err)
2288+
}
2289+
f.Close()
2290+
}

src/os/root_windows_test.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,3 +228,22 @@ func TestRootSymlinkToDirectory(t *testing.T) {
228228
})
229229
}
230230
}
231+
232+
func TestRootOpenFileTruncateNamedPipe(t *testing.T) {
233+
t.Parallel()
234+
name := pipeName()
235+
pipe := newBytePipe(t, name, false)
236+
defer pipe.Close()
237+
238+
root, err := os.OpenRoot(filepath.Dir(name))
239+
if err != nil {
240+
t.Fatal(err)
241+
}
242+
defer root.Close()
243+
244+
f, err := root.OpenFile(filepath.Base(name), os.O_TRUNC|os.O_RDWR|os.O_CREATE, 0666)
245+
if err != nil {
246+
t.Fatal(err)
247+
}
248+
f.Close()
249+
}

src/syscall/syscall_windows.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -468,6 +468,14 @@ func Open(name string, flag int, perm uint32) (fd Handle, err error) {
468468
if flag&O_TRUNC == O_TRUNC &&
469469
(createmode == OPEN_EXISTING || (createmode == OPEN_ALWAYS && err == ERROR_ALREADY_EXISTS)) {
470470
err = Ftruncate(h, 0)
471+
if err == _ERROR_INVALID_PARAMETER {
472+
// ERROR_INVALID_PARAMETER means truncation is not supported on this file handle.
473+
// Unix's O_TRUNC specification says to ignore O_TRUNC on named pipes and terminal devices.
474+
// We do the same here.
475+
if t, err1 := GetFileType(h); err1 == nil && (t == FILE_TYPE_PIPE || t == FILE_TYPE_CHAR) {
476+
err = nil
477+
}
478+
}
471479
if err != nil {
472480
CloseHandle(h)
473481
return InvalidHandle, err

src/syscall/types_windows.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@ const (
3434
WSAECONNRESET Errno = 10054
3535
)
3636

37+
const (
38+
_ERROR_INVALID_PARAMETER Errno = 87
39+
)
40+
3741
const (
3842
// Invented values to support what package os expects.
3943
O_RDONLY = 0x00000

0 commit comments

Comments
 (0)