return netfd, nil
}
+// Defined in os package.
+func newUnixFile(fd uintptr, name string) *os.File
+
func (fd *netFD) dup() (f *os.File, err error) {
ns, call, err := fd.pfd.Dup()
if err != nil {
return nil, err
}
- return os.NewFile(uintptr(ns), fd.name()), nil
+ return newUnixFile(uintptr(ns), fd.name()), nil
}
--- /dev/null
+// Copyright 2023 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/syscall/unix"
+ "testing"
+)
+
+// For backward compatibility, opening a net.Conn, turning it into an os.File,
+// and calling the Fd method should return a blocking descriptor.
+func TestFileFdBlocks(t *testing.T) {
+ ls := newLocalServer(t, "unix")
+ defer ls.teardown()
+
+ errc := make(chan error, 1)
+ done := make(chan bool)
+ handler := func(ls *localServer, ln Listener) {
+ server, err := ln.Accept()
+ errc <- err
+ if err != nil {
+ return
+ }
+ defer server.Close()
+ <-done
+ }
+ if err := ls.buildup(handler); err != nil {
+ t.Fatal(err)
+ }
+ defer close(done)
+
+ client, err := Dial(ls.Listener.Addr().Network(), ls.Listener.Addr().String())
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer client.Close()
+
+ if err := <-errc; err != nil {
+ t.Fatalf("server error: %v", err)
+ }
+
+ // The socket should be non-blocking.
+ rawconn, err := client.(*UnixConn).SyscallConn()
+ if err != nil {
+ t.Fatal(err)
+ }
+ err = rawconn.Control(func(fd uintptr) {
+ nonblock, err := unix.IsNonblock(int(fd))
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !nonblock {
+ t.Fatal("unix socket is in blocking mode")
+ }
+ })
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ file, err := client.(*UnixConn).File()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ // At this point the descriptor should still be non-blocking.
+ rawconn, err = file.SyscallConn()
+ if err != nil {
+ t.Fatal(err)
+ }
+ err = rawconn.Control(func(fd uintptr) {
+ nonblock, err := unix.IsNonblock(int(fd))
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !nonblock {
+ t.Fatal("unix socket as os.File is in blocking mode")
+ }
+ })
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ fd := file.Fd()
+
+ // Calling Fd should have put the descriptor into blocking mode.
+ nonblock, err := unix.IsNonblock(int(fd))
+ if err != nil {
+ t.Fatal(err)
+ }
+ if nonblock {
+ t.Error("unix socket through os.File.Fd is non-blocking")
+ }
+}
"io/fs"
"runtime"
"syscall"
+ _ "unsafe" // for go:linkname
)
const _UTIME_OMIT = unix.UTIME_OMIT
return f
}
+// net_newUnixFile is a hidden entry point called by net.conn.File.
+// This is used so that a nonblocking network connection will become
+// blocking if code calls the Fd method. We don't want that for direct
+// calls to NewFile: passing a nonblocking descriptor to NewFile should
+// remain nonblocking if you get it back using Fd. But for net.conn.File
+// the call to NewFile is hidden from the user. Historically in that case
+// the Fd method has returned a blocking descriptor, and we want to
+// retain that behavior because existing code expects it and depends on it.
+//
+//go:linkname net_newUnixFile net.newUnixFile
+func net_newUnixFile(fd uintptr, name string) *File {
+ f := newFile(fd, name, kindNonBlock)
+ f.nonblock = true // tell Fd to return blocking descriptor
+ return f
+}
+
// newFileKind describes the kind of file to newFile.
type newFileKind int