]> Cypherpunks repositories - gostls13.git/commitdiff
net: Implement FileListener, FileConn, and File methods for Plan 9
authorAkshat Kumar <seed@mail.nanosouffle.net>
Tue, 26 Feb 2013 00:26:40 +0000 (01:26 +0100)
committerRon Minnich <rminnich@gmail.com>
Tue, 26 Feb 2013 00:26:40 +0000 (01:26 +0100)
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

src/pkg/net/fd_plan9.go
src/pkg/net/file_plan9.go
src/pkg/net/tcpsock_plan9.go

index dc5e44ca44195fccdda3dc32cdbce8106d73b782..169087999d50570906740c7ac59f7c9959e66b4a 100644 (file)
@@ -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 {
index ae3ac156b9878d02dc23ada735475e07f4a7d67c..f6ee1c29e0f2a90a7a22695836a423fb4f03debb 100644 (file)
 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
index 26da11a061f9c242312c8e84839ec639e59ca12e..e0dd37f40f3c046a6b4933413138597652d73f44 100644 (file)
@@ -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