diff --git a/src/builtin/builtin.go b/src/builtin/builtin.go index 215c59c4ae8156aa37a399682ccc099c3230b091..af01aea5dd79f4713cbdf52d3d03c5ff629b5380 100644 --- a/src/builtin/builtin.go +++ b/src/builtin/builtin.go @@ -247,7 +247,7 @@ func imag(c ComplexType) FloatType // to the zero value of the respective element type. If the argument // type is a type parameter, the type parameter's type set must // contain only map or slice types, and clear performs the operation -// implied by the type argument. +// implied by the type argument. If t is nil, clear is a no-op. func clear[T ~[]Type | ~map[Type]Type1](t T) // The close built-in function closes a channel, which must be either diff --git a/src/cmd/cgo/doc.go b/src/cmd/cgo/doc.go index 9ff5fd41901c9034d72dc04f01b0795f1b82eda5..ef5272299bbf077ce3167d1d0f37818e6471e28d 100644 --- a/src/cmd/cgo/doc.go +++ b/src/cmd/cgo/doc.go @@ -796,7 +796,7 @@ Instead, the build process generates an object file using dynamic linkage to the desired libraries. The main function is provided by _cgo_main.c: - int main() { return 0; } + int main(int argc, char **argv) { return 0; } void crosscall2(void(*fn)(void*), void *a, int c, uintptr_t ctxt) { } uintptr_t _cgo_wait_runtime_init_done(void) { return 0; } void _cgo_release_context(uintptr_t ctxt) { } diff --git a/src/cmd/cgo/out.go b/src/cmd/cgo/out.go index 954c4b70c954ebbf884b66443d828686df24c92a..5e67cc2d3345985d9ac114f12cfd50accbd90789 100644 --- a/src/cmd/cgo/out.go +++ b/src/cmd/cgo/out.go @@ -59,7 +59,7 @@ func (p *Package) writeDefs() { // Write C main file for using gcc to resolve imports. fmt.Fprintf(fm, "#include <stddef.h>\n") // For size_t below. - fmt.Fprintf(fm, "int main() { return 0; }\n") + fmt.Fprintf(fm, "int main(int argc __attribute__((unused)), char **argv __attribute__((unused))) { return 0; }\n") if *importRuntimeCgo { fmt.Fprintf(fm, "void crosscall2(void(*fn)(void*) __attribute__((unused)), void *a __attribute__((unused)), int c __attribute__((unused)), size_t ctxt __attribute__((unused))) { }\n") fmt.Fprintf(fm, "size_t _cgo_wait_runtime_init_done(void) { return 0; }\n") diff --git a/src/internal/poll/sendfile_unix.go b/src/internal/poll/sendfile_unix.go index f5aee38a054683f6c98abb750d51e1de89e4cc99..1105e0569110fb68afbd0aecb55c989a6ee5748e 100644 --- a/src/internal/poll/sendfile_unix.go +++ b/src/internal/poll/sendfile_unix.go @@ -110,12 +110,20 @@ func sendFile(dstFD *FD, src int, offset *int64, size int64) (written int64, err // Retry. case syscall.ENOSYS, syscall.EOPNOTSUPP, syscall.EINVAL: // ENOSYS indicates no kernel support for sendfile. - // EINVAL indicates a FD type which does not support sendfile. + // EINVAL indicates a FD type that does not support sendfile. // // On Linux, copy_file_range can return EOPNOTSUPP when copying // to a NFS file (issue #40731); check for it here just in case. return written, err, written > 0 default: + // We want to handle ENOTSUP like EOPNOTSUPP. + // It's a pain to put it as a switch case + // because on Linux systems ENOTSUP == EOPNOTSUPP, + // so the compiler complains about a duplicate case. + if err == syscall.ENOTSUP { + return written, err, written > 0 + } + // Not a retryable error. return written, err, true } diff --git a/src/net/sendfile_unix_test.go b/src/net/sendfile_unix_test.go new file mode 100644 index 0000000000000000000000000000000000000000..79fb23b31010d569bbd7c431432275a77716b42b --- /dev/null +++ b/src/net/sendfile_unix_test.go @@ -0,0 +1,86 @@ +// Copyright 2024 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build unix + +package net + +import ( + "internal/testpty" + "io" + "os" + "sync" + "syscall" + "testing" +) + +// Issue 70763: test that we don't fail on sendfile from a tty. +func TestCopyFromTTY(t *testing.T) { + pty, ttyName, err := testpty.Open() + if err != nil { + t.Skipf("skipping test because pty open failed: %v", err) + } + defer pty.Close() + + // Use syscall.Open so that the tty is blocking. + ttyFD, err := syscall.Open(ttyName, syscall.O_RDWR, 0) + if err != nil { + t.Skipf("skipping test because tty open failed: %v", err) + } + defer syscall.Close(ttyFD) + + tty := os.NewFile(uintptr(ttyFD), "tty") + defer tty.Close() + + ln := newLocalListener(t, "tcp") + defer ln.Close() + + ch := make(chan bool) + + const data = "data\n" + + var wg sync.WaitGroup + defer wg.Wait() + + wg.Add(1) + go func() { + defer wg.Done() + conn, err := ln.Accept() + if err != nil { + t.Error(err) + return + } + defer conn.Close() + + buf := make([]byte, len(data)) + if _, err := io.ReadFull(conn, buf); err != nil { + t.Error(err) + } + + ch <- true + }() + + conn, err := Dial("tcp", ln.Addr().String()) + if err != nil { + t.Fatal(err) + } + defer conn.Close() + + wg.Add(1) + go func() { + defer wg.Done() + if _, err := pty.Write([]byte(data)); err != nil { + t.Error(err) + } + <-ch + if err := pty.Close(); err != nil { + t.Error(err) + } + }() + + lr := io.LimitReader(tty, int64(len(data))) + if _, err := io.Copy(conn, lr); err != nil { + t.Error(err) + } +}