| // Copyright 2011 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. |
| |
| 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][0:3] != "net" { |
| return nil, syscall.EPLAN9 |
| } |
| |
| name := comp[2] |
| switch file := comp[n-1]; file { |
| case "ctl", "clone": |
| fd, err := syscall.Dup(int(f.Fd()), -1) |
| if err != nil { |
| return nil, os.NewSyscallError("dup", err) |
| } |
| defer close(fd) |
| |
| dir := netdir + "/" + comp[n-2] |
| ctl = os.NewFile(uintptr(fd), dir+"/"+file) |
| ctl.Seek(0, io.SeekStart) |
| 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 := netdir + "/" + 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 := netdir + "/" + 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) |
| } |
| |
| func fileConn(f *os.File) (Conn, 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 fileListener(f *os.File) (Listener, 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 |
| } |
| |
| func filePacketConn(f *os.File) (PacketConn, error) { |
| return nil, syscall.EPLAN9 |
| } |