]> Cypherpunks repositories - gostls13.git/commitdiff
net: broken sendfile on SmartOS/Solaris
authorShawn Walker-Salas <shawn.walker@oracle.com>
Fri, 8 Apr 2016 22:59:04 +0000 (15:59 -0700)
committerBrad Fitzpatrick <bradfitz@golang.org>
Tue, 12 Apr 2016 19:00:46 +0000 (19:00 +0000)
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>
src/net/sendfile_solaris.go
src/net/sendfile_test.go [new file with mode: 0644]

index eb9d2d1830ef70f3cfc9d097eda5ec5948f1b7af..20d2cddeea93497a527d04c30ea196e8ed3dc592 100644 (file)
@@ -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 (file)
index 0000000..add2bcb
--- /dev/null
@@ -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)
+       }
+}