]> Cypherpunks repositories - gostls13.git/commitdiff
net: fix inconsistent error values on File
authorMikio Hara <mikioh.mikioh@gmail.com>
Sat, 18 Apr 2015 07:53:55 +0000 (16:53 +0900)
committerMikio Hara <mikioh.mikioh@gmail.com>
Tue, 21 Apr 2015 03:37:41 +0000 (03:37 +0000)
This change fixes inconsistent error values on
File{Conn,Listener,PacketConn} and File method of Conn, Listener.

Updates #4856.

Change-Id: I3197b9277bef0e034427e3a44fa77523acaa2520
Reviewed-on: https://go-review.googlesource.com/9101
Reviewed-by: Ian Lance Taylor <iant@golang.org>
14 files changed:
src/net/error_test.go
src/net/fd_plan9.go
src/net/fd_unix.go
src/net/fd_windows.go
src/net/file.go [new file with mode: 0644]
src/net/file_plan9.go
src/net/file_stub.go
src/net/file_unix.go
src/net/file_windows.go
src/net/net.go
src/net/tcpsock_plan9.go
src/net/tcpsock_posix.go
src/net/unixsock_plan9.go
src/net/unixsock_posix.go

index 7c12cba76294a3d52acc55a7acb09e524a89cdbd..03c646c7c99b01e81328250e7c57851d6f608e2d 100644 (file)
@@ -7,6 +7,7 @@ package net
 import (
        "fmt"
        "io"
+       "io/ioutil"
        "net/internal/socktest"
        "os"
        "runtime"
@@ -56,6 +57,10 @@ func (e *OpError) isValid() error {
                if addr == nil {
                        return fmt.Errorf("OpError.Addr is empty: %v", e)
                }
+       case fileAddr:
+               if addr == "" {
+                       return fmt.Errorf("OpError.Addr is empty: %v", e)
+               }
        }
        if e.Err == nil {
                return fmt.Errorf("OpError.Err is empty: %v", e)
@@ -503,3 +508,112 @@ func TestAcceptError(t *testing.T) {
        time.Sleep(100 * time.Millisecond)
        ls.teardown()
 }
+
+// parseCommonError parses nestedErr and reports whether it is a valid
+// error value from miscellaneous functions.
+// It returns nil when nestedErr is valid.
+func parseCommonError(nestedErr error) error {
+       if nestedErr == nil {
+               return nil
+       }
+
+       switch err := nestedErr.(type) {
+       case *OpError:
+               if err := err.isValid(); err != nil {
+                       return err
+               }
+               nestedErr = err.Err
+               goto second
+       }
+       return fmt.Errorf("unexpected type on 1st nested level: %T", nestedErr)
+
+second:
+       if isPlatformError(nestedErr) {
+               return nil
+       }
+       switch err := nestedErr.(type) {
+       case *os.SyscallError:
+               nestedErr = err.Err
+               goto third
+       case *os.LinkError:
+               nestedErr = err.Err
+               goto third
+       case *os.PathError:
+               nestedErr = err.Err
+               goto third
+       }
+       return fmt.Errorf("unexpected type on 2nd nested level: %T", nestedErr)
+
+third:
+       if isPlatformError(nestedErr) {
+               return nil
+       }
+       return fmt.Errorf("unexpected type on 3rd nested level: %T", nestedErr)
+}
+
+func TestFileError(t *testing.T) {
+       switch runtime.GOOS {
+       case "windows":
+               t.Skip("not supported on %s", runtime.GOOS)
+       }
+
+       f, err := ioutil.TempFile("", "nettest")
+       if err != nil {
+               t.Fatal(err)
+       }
+       defer f.Close()
+
+       c, err := FileConn(f)
+       if err != nil {
+               if c != nil {
+                       t.Errorf("FileConn returned non-nil interface %T(%v) with err != nil", c, c)
+               }
+               if perr := parseCommonError(err); perr != nil {
+                       t.Error(perr)
+               }
+       } else {
+               c.Close()
+               t.Error("should fail")
+       }
+       ln, err := FileListener(f)
+       if err != nil {
+               if ln != nil {
+                       t.Errorf("FileListener returned non-nil interface %T(%v) with err != nil", ln, ln)
+               }
+               if perr := parseCommonError(err); perr != nil {
+                       t.Error(perr)
+               }
+       } else {
+               ln.Close()
+               t.Error("should fail")
+       }
+       pc, err := FilePacketConn(f)
+       if err != nil {
+               if pc != nil {
+                       t.Errorf("FilePacketConn returned non-nil interface %T(%v) with err != nil", pc, pc)
+               }
+               if perr := parseCommonError(err); perr != nil {
+                       t.Error(perr)
+               }
+       } else {
+               pc.Close()
+               t.Error("should fail")
+       }
+
+       ln, err = newLocalListener("tcp")
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       for i := 0; i < 3; i++ {
+               f, err := ln.(*TCPListener).File()
+               if err != nil {
+                       if perr := parseCommonError(err); perr != nil {
+                               t.Error(perr)
+                       }
+               } else {
+                       f.Close()
+               }
+               ln.Close()
+       }
+}
index 29ec8012788a367cbff92962e22f65a7ae20a2be..347829ce8e0b07cf87a32e23d3a9390ee9c2b7da 100644 (file)
@@ -202,7 +202,7 @@ func (fd *netFD) file(f *os.File, s string) (*os.File, error) {
        dfd, err := syscall.Dup(int(f.Fd()), -1)
        syscall.ForkLock.RUnlock()
        if err != nil {
-               return nil, &OpError{"dup", s, fd.laddr, err}
+               return nil, err
        }
        return os.NewFile(uintptr(dfd), s), nil
 }
index 329819e80a1f7cf75a2da9c27ee75713297fd7f8..4b19d9442c8e1ce1191a031b11c171a1a5d438b7 100644 (file)
@@ -459,7 +459,7 @@ func dupCloseOnExecOld(fd int) (newfd int, err error) {
 func (fd *netFD) dup() (f *os.File, err error) {
        ns, err := dupCloseOnExec(fd.sysfd)
        if err != nil {
-               return nil, &OpError{"dup", fd.net, fd.laddr, err}
+               return nil, err
        }
 
        // We want blocking mode for the new fd, hence the double negative.
@@ -467,7 +467,7 @@ func (fd *netFD) dup() (f *os.File, err error) {
        // I/O will block the thread instead of letting us use the epoll server.
        // Everything will still work, just with more threads.
        if err = syscall.SetNonblock(ns, false); err != nil {
-               return nil, &OpError{"setnonblock", fd.net, fd.laddr, err}
+               return nil, err
        }
 
        return os.NewFile(uintptr(ns), fd.name()), nil
index 4826a882368c991748285045b1d83527a0058a2d..01fe1a9595ba61d441709233df601ab2551dd985 100644 (file)
@@ -605,7 +605,7 @@ func (fd *netFD) accept() (*netFD, error) {
 
 func (fd *netFD) dup() (*os.File, error) {
        // TODO: Implement this
-       return nil, os.NewSyscallError("dup", syscall.EWINDOWS)
+       return nil, syscall.EWINDOWS
 }
 
 func (fd *netFD) readMsg(p []byte, oob []byte) (n, oobn, flags int, sa syscall.Sockaddr, err error) {
diff --git a/src/net/file.go b/src/net/file.go
new file mode 100644 (file)
index 0000000..be93e2c
--- /dev/null
@@ -0,0 +1,48 @@
+// Copyright 2015 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 "os"
+
+type fileAddr string
+
+func (fileAddr) Network() string  { return "file+net" }
+func (f fileAddr) String() string { return string(f) }
+
+// 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) {
+       c, err = fileConn(f)
+       if err != nil {
+               err = &OpError{Op: "file", Net: "file+net", Addr: fileAddr(f.Name()), Err: err}
+       }
+       return
+}
+
+// FileListener returns a copy of the network listener corresponding
+// to the open file f.
+// It is the caller's responsibility to close ln when finished.
+// Closing ln does not affect f, and closing f does not affect ln.
+func FileListener(f *os.File) (ln Listener, err error) {
+       ln, err = fileListener(f)
+       if err != nil {
+               err = &OpError{Op: "file", Net: "file+net", Addr: fileAddr(f.Name()), Err: err}
+       }
+       return
+}
+
+// FilePacketConn returns a copy of the packet 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 FilePacketConn(f *os.File) (c PacketConn, err error) {
+       c, err = filePacketConn(f)
+       if err != nil {
+               err = &OpError{Op: "file", Net: "file+net", Addr: fileAddr(f.Name()), Err: err}
+       }
+       return
+}
index 068f0881dd36dd3f7740d7f24960167a0f16df39..0aa6c32d06b511252734a82155412b612bbfe1b2 100644 (file)
@@ -39,7 +39,7 @@ func newFileFD(f *os.File) (net *netFD, err error) {
 
        path, err := syscall.Fd2path(int(f.Fd()))
        if err != nil {
-               return nil, os.NewSyscallError("fd2path", err)
+               return nil, err
        }
        comp := splitAtBytes(path, "/")
        n := len(comp)
@@ -54,7 +54,7 @@ func newFileFD(f *os.File) (net *netFD, err error) {
                fd, err := syscall.Dup(int(f.Fd()), -1)
                syscall.ForkLock.RUnlock()
                if err != nil {
-                       return nil, os.NewSyscallError("dup", err)
+                       return nil, err
                }
                defer close(fd)
 
@@ -86,7 +86,7 @@ func newFileFD(f *os.File) (net *netFD, err error) {
        return newFD(comp[1], name, ctl, nil, laddr, nil)
 }
 
-func newFileConn(f *os.File) (c Conn, err error) {
+func fileConn(f *os.File) (Conn, error) {
        fd, err := newFileFD(f)
        if err != nil {
                return nil, err
@@ -109,7 +109,7 @@ func newFileConn(f *os.File) (c Conn, err error) {
        return nil, syscall.EPLAN9
 }
 
-func newFileListener(f *os.File) (l Listener, err error) {
+func fileListener(f *os.File) (Listener, error) {
        fd, err := newFileFD(f)
        if err != nil {
                return nil, err
@@ -132,26 +132,6 @@ func newFileListener(f *os.File) (l Listener, err error) {
        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 newFileConn(f)
-}
-
-// FileListener returns a copy of the network listener corresponding
-// to the open file f.  It is the caller's responsibility to close l
-// 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 newFileListener(f)
-}
-
-// FilePacketConn returns a copy of the packet 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 FilePacketConn(f *os.File) (c PacketConn, err error) {
+func filePacketConn(f *os.File) (PacketConn, error) {
        return nil, syscall.EPLAN9
 }
index 4281072ef932c0993fd08a841abc0e2c3af2fd04..0f7460c757974d7cb05a2aaaa0ba18552c3e16d3 100644 (file)
@@ -11,28 +11,6 @@ import (
        "syscall"
 )
 
-// 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.ENOPROTOOPT
-
-}
-
-// FileListener returns a copy of the network listener corresponding
-// to the open file f.  It is the caller's responsibility to close l
-// 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.ENOPROTOOPT
-
-}
-
-// FilePacketConn returns a copy of the packet 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 FilePacketConn(f *os.File) (c PacketConn, err error) {
-       return nil, syscall.ENOPROTOOPT
-}
+func fileConn(f *os.File) (Conn, error)             { return nil, syscall.ENOPROTOOPT }
+func fileListener(f *os.File) (Listener, error)     { return nil, syscall.ENOPROTOOPT }
+func filePacketConn(f *os.File) (PacketConn, error) { return nil, syscall.ENOPROTOOPT }
index 8d806a1d6342099a139f5d2bff9903bdc052fb7d..98ceea1d55caa26796de5e371ef6951c4efb602c 100644 (file)
@@ -14,7 +14,7 @@ import (
 func newFileFD(f *os.File) (*netFD, error) {
        fd, err := dupCloseOnExec(int(f.Fd()))
        if err != nil {
-               return nil, os.NewSyscallError("dup", err)
+               return nil, err
        }
 
        if err = syscall.SetNonblock(fd, true); err != nil {
@@ -25,16 +25,13 @@ func newFileFD(f *os.File) (*netFD, error) {
        sotype, err := syscall.GetsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_TYPE)
        if err != nil {
                closeFunc(fd)
-               return nil, os.NewSyscallError("getsockopt", err)
+               return nil, err
        }
 
        family := syscall.AF_UNSPEC
        toAddr := sockaddrToTCP
        lsa, _ := syscall.Getsockname(fd)
        switch lsa.(type) {
-       default:
-               closeFunc(fd)
-               return nil, syscall.EINVAL
        case *syscall.SockaddrInet4:
                family = syscall.AF_INET
                if sotype == syscall.SOCK_DGRAM {
@@ -57,6 +54,9 @@ func newFileFD(f *os.File) (*netFD, error) {
                } else if sotype == syscall.SOCK_SEQPACKET {
                        toAddr = sockaddrToUnixpacket
                }
+       default:
+               closeFunc(fd)
+               return nil, syscall.EPROTONOSUPPORT
        }
        laddr := toAddr(lsa)
        rsa, _ := syscall.Getpeername(fd)
@@ -75,11 +75,7 @@ func newFileFD(f *os.File) (*netFD, error) {
        return netfd, 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) {
+func fileConn(f *os.File) (Conn, error) {
        fd, err := newFileFD(f)
        if err != nil {
                return nil, err
@@ -98,11 +94,7 @@ func FileConn(f *os.File) (c Conn, err error) {
        return nil, syscall.EINVAL
 }
 
-// FileListener returns a copy of the network listener corresponding
-// to the open file f.  It is the caller's responsibility to close l
-// when finished.  Closing l does not affect f, and closing f does not
-// affect l.
-func FileListener(f *os.File) (l Listener, err error) {
+func fileListener(f *os.File) (Listener, error) {
        fd, err := newFileFD(f)
        if err != nil {
                return nil, err
@@ -117,11 +109,7 @@ func FileListener(f *os.File) (l Listener, err error) {
        return nil, syscall.EINVAL
 }
 
-// FilePacketConn returns a copy of the packet 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 FilePacketConn(f *os.File) (c PacketConn, err error) {
+func filePacketConn(f *os.File) (PacketConn, error) {
        fd, err := newFileFD(f)
        if err != nil {
                return nil, err
index ca2b9b2262c02d6893a1e3074383dd4ed6e633df..241fa17617c2bf20073d275c479e01ed288dde58 100644 (file)
@@ -9,29 +9,17 @@ import (
        "syscall"
 )
 
-// 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) {
+func fileConn(f *os.File) (Conn, error) {
        // TODO: Implement this
-       return nil, os.NewSyscallError("FileConn", syscall.EWINDOWS)
+       return nil, syscall.EWINDOWS
 }
 
-// FileListener returns a copy of the network listener corresponding
-// to the open file f.  It is the caller's responsibility to close l
-// when finished.  Closing l does not affect f, and closing f does not
-// affect l.
-func FileListener(f *os.File) (l Listener, err error) {
+func fileListener(f *os.File) (Listener, error) {
        // TODO: Implement this
-       return nil, os.NewSyscallError("FileListener", syscall.EWINDOWS)
+       return nil, syscall.EWINDOWS
 }
 
-// FilePacketConn returns a copy of the packet 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 FilePacketConn(f *os.File) (c PacketConn, err error) {
+func filePacketConn(f *os.File) (PacketConn, error) {
        // TODO: Implement this
-       return nil, os.NewSyscallError("FilePacketConn", syscall.EWINDOWS)
+       return nil, syscall.EWINDOWS
 }
index 83739b63133421092576c69a7cc0b18a4c28c3b8..d1029832bf81004cf90701baa353a6670a0bb1a7 100644 (file)
@@ -236,7 +236,13 @@ func (c *conn) SetWriteBuffer(bytes int) error {
 // The returned os.File's file descriptor is different from the connection's.
 // Attempting to change properties of the original using this duplicate
 // may or may not have the desired effect.
-func (c *conn) File() (f *os.File, err error) { return c.fd.dup() }
+func (c *conn) File() (f *os.File, err error) {
+       f, err = c.fd.dup()
+       if err != nil {
+               err = &OpError{Op: "file", Net: c.fd.net, Addr: c.fd.laddr, Err: err}
+       }
+       return
+}
 
 // An Error represents a network error.
 type Error interface {
index 2390cbddb8112587d46de6acbe2b77d02fa48489..deb2424c27ac4cf94dd58f12ab1c09a3fe51f268 100644 (file)
@@ -193,7 +193,13 @@ func (l *TCPListener) SetDeadline(t time.Time) error {
 // The returned os.File's file descriptor is different from the
 // connection's.  Attempting to change properties of the original
 // using this duplicate may or may not have the desired effect.
-func (l *TCPListener) File() (f *os.File, err error) { return l.dup() }
+func (l *TCPListener) File() (f *os.File, err error) {
+       f, err = l.dup()
+       if err != nil {
+               err = &OpError{Op: "file", Net: l.fd.net, Addr: l.fd.laddr, Err: err}
+       }
+       return
+}
 
 // ListenTCP announces on the TCP address laddr and returns a TCP
 // listener.  Net must be "tcp", "tcp4", or "tcp6".  If laddr has a
index da4dd50257a11875b5f8b96d25a0663065120181..78a3b8bf83a11707e19a0c401b43dbec1fb60b9f 100644 (file)
@@ -290,7 +290,13 @@ func (l *TCPListener) SetDeadline(t time.Time) error {
 // The returned os.File's file descriptor is different from the
 // connection's.  Attempting to change properties of the original
 // using this duplicate may or may not have the desired effect.
-func (l *TCPListener) File() (f *os.File, err error) { return l.fd.dup() }
+func (l *TCPListener) File() (f *os.File, err error) {
+       f, err = l.fd.dup()
+       if err != nil {
+               err = &OpError{Op: "file", Net: l.fd.net, Addr: l.fd.laddr, Err: err}
+       }
+       return
+}
 
 // ListenTCP announces on the TCP address laddr and returns a TCP
 // listener.  Net must be "tcp", "tcp4", or "tcp6".  If laddr has a
index 29727020045b8f7db89b099c59d1ade2d613ef5d..410933ddd1e7e0ffe4079eeec602dc63948ebedf 100644 (file)
@@ -133,7 +133,7 @@ func (l *UnixListener) SetDeadline(t time.Time) error {
 // connection's.  Attempting to change properties of the original
 // using this duplicate may or may not have the desired effect.
 func (l *UnixListener) File() (*os.File, error) {
-       return nil, syscall.EPLAN9
+       return nil, &OpError{Op: "file", Net: "<nil>", Addr: nil, Err: syscall.EPLAN9}
 }
 
 // ListenUnixgram listens for incoming Unix datagram packets addressed
index 2881437ec0cd51a24faaf0d7d741eacbdf383ce2..5cb2f436b618f2217dd2c233dfb8c987db6df43e 100644 (file)
@@ -370,7 +370,13 @@ func (l *UnixListener) SetDeadline(t time.Time) (err error) {
 // The returned os.File's file descriptor is different from the
 // connection's.  Attempting to change properties of the original
 // using this duplicate may or may not have the desired effect.
-func (l *UnixListener) File() (f *os.File, err error) { return l.fd.dup() }
+func (l *UnixListener) File() (f *os.File, err error) {
+       f, err = l.fd.dup()
+       if err != nil {
+               err = &OpError{Op: "file", Net: l.fd.net, Addr: l.fd.laddr, Err: err}
+       }
+       return
+}
 
 // ListenUnixgram listens for incoming Unix datagram packets addressed
 // to the local address laddr.  The network net must be "unixgram".