internal/poll: in SendFile treat ENOTSUP like EOPNOTSUPP

Fixes #70763

Change-Id: Ifb79b5b0529f7977df0fe1b59d224b8b31df2c9b
Reviewed-on: https://go-review.googlesource.com/c/go/+/635396
Reviewed-by: Ian Lance Taylor <iant@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Damien Neil <dneil@google.com>
Auto-Submit: Ian Lance Taylor <iant@google.com>
This commit is contained in:
Ian Lance Taylor 2024-12-11 14:02:28 -08:00 committed by Gopher Robot
parent fafd4477f3
commit 077d51909d
2 changed files with 95 additions and 1 deletions

View file

@ -110,12 +110,20 @@ func sendFile(dstFD *FD, src int, offset *int64, size int64) (written int64, err
// Retry. // Retry.
case syscall.ENOSYS, syscall.EOPNOTSUPP, syscall.EINVAL: case syscall.ENOSYS, syscall.EOPNOTSUPP, syscall.EINVAL:
// ENOSYS indicates no kernel support for sendfile. // 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 // On Linux, copy_file_range can return EOPNOTSUPP when copying
// to a NFS file (issue #40731); check for it here just in case. // to a NFS file (issue #40731); check for it here just in case.
return written, err, written > 0 return written, err, written > 0
default: 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. // Not a retryable error.
return written, err, true return written, err, true
} }

View file

@ -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)
}
}