diff options
| author | Shawn Walker-Salas <shawn.walker@oracle.com> | 2016-04-08 15:59:04 -0700 |
|---|---|---|
| committer | Brad Fitzpatrick <bradfitz@golang.org> | 2016-04-12 19:00:46 +0000 |
| commit | 98080a6c64c2d9bc2a759b66a9b861af4ef7367b (patch) | |
| tree | bf486e483620060bd181d7280ef81cc437592842 /src | |
| parent | b1851a3c11a179d4eb55f9d0dd25ef81668a9f81 (diff) | |
| download | go-98080a6c64c2d9bc2a759b66a9b861af4ef7367b.tar.xz | |
net: broken sendfile on SmartOS/Solaris
In the event of a partial write on Solaris and some BSDs, the offset
pointer passed to sendfile() will be updated even though the function
returns -1 if errno is set to EAGAIN/EINTR. In that case, calculate the
bytes written based on the difference between the updated offset and the
original offset. If no bytes were written, and errno is set to
EAGAIN/EINTR, ignore the errno.
Fixes #13892
Change-Id: I6334b5ef2edcbebdaa7db36fa4f7785967313c2d
Reviewed-on: https://go-review.googlesource.com/21769
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
Diffstat (limited to 'src')
| -rw-r--r-- | src/net/sendfile_solaris.go | 9 | ||||
| -rw-r--r-- | src/net/sendfile_test.go | 90 |
2 files changed, 97 insertions, 2 deletions
diff --git a/src/net/sendfile_solaris.go b/src/net/sendfile_solaris.go index eb9d2d1830..20d2cddeea 100644 --- a/src/net/sendfile_solaris.go +++ b/src/net/sendfile_solaris.go @@ -26,8 +26,6 @@ const maxSendfileSize int = 4 << 20 // // if handled == false, sendFile performed no work. func sendFile(c *netFD, r io.Reader) (written int64, err error, handled bool) { - return // Solaris sendfile is disabled until Issue 13892 is understood and fixed - // Solaris uses 0 as the "until EOF" value. If you pass in more bytes than the // file contains, it will loop back to the beginning ad nauseam until it's sent // exactly the number of bytes told to. As such, we need to know exactly how many @@ -78,6 +76,13 @@ func sendFile(c *netFD, r io.Reader) (written int64, err error, handled bool) { } pos1 := pos n, err1 := syscall.Sendfile(dst, src, &pos1, n) + if err1 == syscall.EAGAIN || err1 == syscall.EINTR { + // partial write may have occurred + if n = int(pos1 - pos); n == 0 { + // nothing more to write + err1 = nil + } + } if n > 0 { pos += int64(n) written += int64(n) diff --git a/src/net/sendfile_test.go b/src/net/sendfile_test.go new file mode 100644 index 0000000000..add2bcb5c6 --- /dev/null +++ b/src/net/sendfile_test.go @@ -0,0 +1,90 @@ +// Copyright 2016 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 ( + "crypto/sha256" + "encoding/hex" + "fmt" + "io" + "os" + "testing" +) + +const ( + twain = "../compress/testdata/Mark.Twain-Tom.Sawyer.txt" + twainLen = 387851 + twainSHA256 = "461eb7cb2d57d293fc680c836464c9125e4382be3596f7d415093ae9db8fcb0e" +) + +func TestSendFile(t *testing.T) { + ln, err := newLocalListener("tcp") + if err != nil { + t.Fatal(err) + } + defer ln.Close() + + errc := make(chan error, 1) + go func(ln Listener) { + // Wait for a connection. + conn, err := ln.Accept() + if err != nil { + errc <- err + close(errc) + return + } + + go func() { + defer close(errc) + defer conn.Close() + + f, err := os.Open(twain) + if err != nil { + errc <- err + return + } + defer f.Close() + + // Return file data using io.Copy, which should use + // sendFile if available. + sbytes, err := io.Copy(conn, f) + if err != nil { + errc <- err + return + } + + if sbytes != twainLen { + errc <- fmt.Errorf("sent %d bytes; expected %d", sbytes, twainLen) + return + } + }() + }(ln) + + // Connect to listener to retrieve file and verify digest matches + // expected. + c, err := Dial("tcp", ln.Addr().String()) + if err != nil { + t.Fatal(err) + } + defer c.Close() + + h := sha256.New() + rbytes, err := io.Copy(h, c) + if err != nil { + t.Error(err) + } + + if rbytes != twainLen { + t.Errorf("received %d bytes; expected %d", rbytes, twainLen) + } + + if res := hex.EncodeToString(h.Sum(nil)); res != twainSHA256 { + t.Error("retrieved data hash did not match") + } + + for err := range errc { + t.Error(err) + } +} |
