mirror of
https://github.com/golang/go
synced 2024-11-23 00:40:08 -07:00
net: Implement FileListener, FileConn, and File methods for Plan 9
Functions for representing network connections as files and vice versa, on Plan 9. Representing network connections as files is not so straight-forward, because a network connection on Plan 9 is represented by a host of files rather than a single file descriptor (as is the case on UNIX). We use the type system to distinguish between listeners and connections, returning the control file in the former case and the data file in the latter case. R=rsc, rminnich, ality, akumar, bradfitz CC=golang-dev https://golang.org/cl/7235068
This commit is contained in:
parent
31444a796a
commit
b461fe660d
@ -83,8 +83,29 @@ func (fd *netFD) Close() error {
|
||||
return err
|
||||
}
|
||||
|
||||
// This method is only called via Conn.
|
||||
func (fd *netFD) dup() (*os.File, error) {
|
||||
return nil, syscall.EPLAN9
|
||||
if !fd.ok() || fd.data == nil {
|
||||
return nil, syscall.EINVAL
|
||||
}
|
||||
return fd.file(fd.data, fd.dir+"/data")
|
||||
}
|
||||
|
||||
func (l *TCPListener) dup() (*os.File, error) {
|
||||
if !l.fd.ok() {
|
||||
return nil, syscall.EINVAL
|
||||
}
|
||||
return l.fd.file(l.fd.ctl, l.fd.dir+"/ctl")
|
||||
}
|
||||
|
||||
func (fd *netFD) file(f *os.File, s string) (*os.File, error) {
|
||||
syscall.ForkLock.RLock()
|
||||
dfd, err := syscall.Dup(int(f.Fd()), -1)
|
||||
syscall.ForkLock.RUnlock()
|
||||
if err != nil {
|
||||
return nil, &OpError{"dup", s, fd.laddr, err}
|
||||
}
|
||||
return os.NewFile(uintptr(dfd), s), nil
|
||||
}
|
||||
|
||||
func setDeadline(fd *netFD, t time.Time) error {
|
||||
|
@ -5,16 +5,139 @@
|
||||
package net
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"os"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func (fd *netFD) status(ln int) (string, error) {
|
||||
if !fd.ok() {
|
||||
return "", syscall.EINVAL
|
||||
}
|
||||
|
||||
status, err := os.Open(fd.dir + "/status")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer status.Close()
|
||||
buf := make([]byte, ln)
|
||||
n, err := io.ReadFull(status, buf[:])
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(buf[:n]), nil
|
||||
}
|
||||
|
||||
func newFileFD(f *os.File) (net *netFD, err error) {
|
||||
var ctl *os.File
|
||||
close := func(fd int) {
|
||||
if err != nil {
|
||||
syscall.Close(fd)
|
||||
}
|
||||
}
|
||||
|
||||
path, err := syscall.Fd2path(int(f.Fd()))
|
||||
if err != nil {
|
||||
return nil, os.NewSyscallError("fd2path", err)
|
||||
}
|
||||
comp := splitAtBytes(path, "/")
|
||||
n := len(comp)
|
||||
if n < 3 || comp[0] != "net" {
|
||||
return nil, syscall.EPLAN9
|
||||
}
|
||||
|
||||
name := comp[2]
|
||||
switch file := comp[n-1]; file {
|
||||
case "ctl", "clone":
|
||||
syscall.ForkLock.RLock()
|
||||
fd, err := syscall.Dup(int(f.Fd()), -1)
|
||||
syscall.ForkLock.RUnlock()
|
||||
if err != nil {
|
||||
return nil, os.NewSyscallError("dup", err)
|
||||
}
|
||||
defer close(fd)
|
||||
|
||||
dir := "/net/" + comp[n-2]
|
||||
ctl = os.NewFile(uintptr(fd), dir+"/"+file)
|
||||
ctl.Seek(0, 0)
|
||||
var buf [16]byte
|
||||
n, err := ctl.Read(buf[:])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
name = string(buf[:n])
|
||||
default:
|
||||
if len(comp) < 4 {
|
||||
return nil, errors.New("could not find control file for connection")
|
||||
}
|
||||
dir := "/net/" + comp[1] + "/" + name
|
||||
ctl, err = os.OpenFile(dir+"/ctl", os.O_RDWR, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer close(int(ctl.Fd()))
|
||||
}
|
||||
dir := "/net/" + comp[1] + "/" + name
|
||||
laddr, err := readPlan9Addr(comp[1], dir+"/local")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return newFD(comp[1], name, ctl, nil, laddr, nil), nil
|
||||
}
|
||||
|
||||
func newFileConn(f *os.File) (c Conn, err error) {
|
||||
fd, err := newFileFD(f)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !fd.ok() {
|
||||
return nil, syscall.EINVAL
|
||||
}
|
||||
|
||||
fd.data, err = os.OpenFile(fd.dir+"/data", os.O_RDWR, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch fd.laddr.(type) {
|
||||
case *TCPAddr:
|
||||
return newTCPConn(fd), nil
|
||||
case *UDPAddr:
|
||||
return newUDPConn(fd), nil
|
||||
}
|
||||
return nil, syscall.EPLAN9
|
||||
}
|
||||
|
||||
func newFileListener(f *os.File) (l Listener, err error) {
|
||||
fd, err := newFileFD(f)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch fd.laddr.(type) {
|
||||
case *TCPAddr:
|
||||
default:
|
||||
return nil, syscall.EPLAN9
|
||||
}
|
||||
|
||||
// check that file corresponds to a listener
|
||||
s, err := fd.status(len("Listen"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if s != "Listen" {
|
||||
return nil, errors.New("file does not represent a listener")
|
||||
}
|
||||
|
||||
return &TCPListener{fd}, nil
|
||||
}
|
||||
|
||||
// FileConn returns a copy of the network connection corresponding to
|
||||
// the open file f. It is the caller's responsibility to close f when
|
||||
// finished. Closing c does not affect f, and closing f does not
|
||||
// affect c.
|
||||
func FileConn(f *os.File) (c Conn, err error) {
|
||||
return nil, syscall.EPLAN9
|
||||
return newFileConn(f)
|
||||
}
|
||||
|
||||
// FileListener returns a copy of the network listener corresponding
|
||||
@ -22,7 +145,7 @@ func FileConn(f *os.File) (c Conn, err error) {
|
||||
// when finished. Closing l does not affect f, and closing f does not
|
||||
// affect l.
|
||||
func FileListener(f *os.File) (l Listener, err error) {
|
||||
return nil, syscall.EPLAN9
|
||||
return newFileListener(f)
|
||||
}
|
||||
|
||||
// FilePacketConn returns a copy of the packet network connection
|
||||
|
@ -161,7 +161,7 @@ func (l *TCPListener) SetDeadline(t time.Time) error {
|
||||
// File returns a copy of the underlying os.File, set to blocking
|
||||
// mode. It is the caller's responsibility to close f when finished.
|
||||
// Closing l does not affect f, and closing f does not affect l.
|
||||
func (l *TCPListener) File() (f *os.File, err error) { return l.fd.dup() }
|
||||
func (l *TCPListener) File() (f *os.File, err error) { return l.dup() }
|
||||
|
||||
// ListenTCP announces on the TCP address laddr and returns a TCP
|
||||
// listener. Net must be "tcp", "tcp4", or "tcp6". If laddr has a
|
||||
|
Loading…
Reference in New Issue
Block a user