diff --git a/src/os/file.go b/src/os/file.go index 228777c677..fdead63bfc 100644 --- a/src/os/file.go +++ b/src/os/file.go @@ -473,3 +473,12 @@ func (f *File) SetReadDeadline(t time.Time) error { func (f *File) SetWriteDeadline(t time.Time) error { return f.setWriteDeadline(t) } + +// SyscallConn returns a raw file. +// This implements the syscall.Conn interface. +func (f *File) SyscallConn() (syscall.RawConn, error) { + if err := f.checkValid("SyscallConn"); err != nil { + return nil, err + } + return newRawConn(f) +} diff --git a/src/os/file_plan9.go b/src/os/file_plan9.go index 2c74403434..3fa12e6816 100644 --- a/src/os/file_plan9.go +++ b/src/os/file_plan9.go @@ -534,3 +534,21 @@ func (f *File) checkValid(op string) error { } return nil } + +type rawConn struct{} + +func (c *rawConn) Control(f func(uintptr)) error { + return syscall.EPLAN9 +} + +func (c *rawConn) Read(f func(uintptr) bool) error { + return syscall.EPLAN9 +} + +func (c *rawConn) Write(f func(uintptr) bool) error { + return syscall.EPLAN9 +} + +func newRawConn(file *File) (*rawConn, error) { + return nil, syscall.EPLAN9 +} diff --git a/src/os/os_unix_test.go b/src/os/os_unix_test.go index 0317f7257e..2aa930ea80 100644 --- a/src/os/os_unix_test.go +++ b/src/os/os_unix_test.go @@ -22,6 +22,9 @@ func init() { isReadonlyError = func(err error) bool { return err == syscall.EROFS } } +// For TestRawConnReadWrite. +type syscallDescriptor = int + func checkUidGid(t *testing.T, path string, uid, gid int) { dir, err := Lstat(path) if err != nil { diff --git a/src/os/os_windows_test.go b/src/os/os_windows_test.go index 1023b25e22..285e1eb35e 100644 --- a/src/os/os_windows_test.go +++ b/src/os/os_windows_test.go @@ -26,6 +26,9 @@ import ( "unsafe" ) +// For TestRawConnReadWrite. +type syscallDescriptor = syscall.Handle + func TestSameWindowsFile(t *testing.T) { temp, err := ioutil.TempDir("", "TestSameWindowsFile") if err != nil { diff --git a/src/os/rawconn.go b/src/os/rawconn.go new file mode 100644 index 0000000000..9e11cda8c9 --- /dev/null +++ b/src/os/rawconn.go @@ -0,0 +1,47 @@ +// Copyright 2018 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. + +// +build !plan9 + +package os + +import ( + "runtime" +) + +// rawConn implements syscall.RawConn. +type rawConn struct { + file *File +} + +func (c *rawConn) Control(f func(uintptr)) error { + if err := c.file.checkValid("SyscallConn.Control"); err != nil { + return err + } + err := c.file.pfd.RawControl(f) + runtime.KeepAlive(c.file) + return err +} + +func (c *rawConn) Read(f func(uintptr) bool) error { + if err := c.file.checkValid("SyscallConn.Read"); err != nil { + return err + } + err := c.file.pfd.RawRead(f) + runtime.KeepAlive(c.file) + return err +} + +func (c *rawConn) Write(f func(uintptr) bool) error { + if err := c.file.checkValid("SyscallConn.Write"); err != nil { + return err + } + err := c.file.pfd.RawWrite(f) + runtime.KeepAlive(c.file) + return err +} + +func newRawConn(file *File) (*rawConn, error) { + return &rawConn{file: file}, nil +} diff --git a/src/os/rawconn_test.go b/src/os/rawconn_test.go new file mode 100644 index 0000000000..820150d959 --- /dev/null +++ b/src/os/rawconn_test.go @@ -0,0 +1,65 @@ +// Copyright 2018 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. + +// Test use of raw connections. +// +build !plan9,!nacl,!js + +package os_test + +import ( + "os" + "syscall" + "testing" +) + +func TestRawConnReadWrite(t *testing.T) { + t.Parallel() + + r, w, err := os.Pipe() + if err != nil { + t.Fatal(err) + } + defer r.Close() + defer w.Close() + + rconn, err := r.SyscallConn() + if err != nil { + t.Fatal(err) + } + wconn, err := w.SyscallConn() + if err != nil { + t.Fatal(err) + } + + var operr error + err = wconn.Write(func(s uintptr) bool { + _, operr = syscall.Write(syscallDescriptor(s), []byte{'b'}) + return operr != syscall.EAGAIN + }) + if err != nil { + t.Fatal(err) + } + if operr != nil { + t.Fatal(err) + } + + var n int + buf := make([]byte, 1) + err = rconn.Read(func(s uintptr) bool { + n, operr = syscall.Read(syscallDescriptor(s), buf) + return operr != syscall.EAGAIN + }) + if err != nil { + t.Fatal(err) + } + if operr != nil { + t.Fatal(operr) + } + if n != 1 { + t.Errorf("read %d bytes, expected 1", n) + } + if buf[0] != 'b' { + t.Errorf("read %q, expected %q", buf, "b") + } +} diff --git a/src/syscall/net.go b/src/syscall/net.go index 272d3afc38..531fa80d8f 100644 --- a/src/syscall/net.go +++ b/src/syscall/net.go @@ -26,7 +26,7 @@ type RawConn interface { Write(f func(fd uintptr) (done bool)) error } -// Conn is implemented by some types in the net package to provide +// Conn is implemented by some types in the net and os packages to provide // access to the underlying file descriptor or handle. type Conn interface { // SyscallConn returns a raw network connection.