]> Cypherpunks repositories - gostls13.git/commitdiff
internal/poll: handle (0, EINVAL) return from sendfile on Solaris
authorDamien Neil <dneil@google.com>
Tue, 29 Oct 2024 17:31:23 +0000 (10:31 -0700)
committerGopher Robot <gobot@golang.org>
Wed, 30 Oct 2024 19:53:06 +0000 (19:53 +0000)
Also check for GOOS=illumos as well as GOOS=solaris.

Change-Id: I887e6cddc1b8ad0f4624c9491e089c6bb8bce70e
Reviewed-on: https://go-review.googlesource.com/c/go/+/622977
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Ian Lance Taylor <iant@google.com>
Commit-Queue: Ian Lance Taylor <iant@google.com>
Auto-Submit: Ian Lance Taylor <iant@google.com>

src/internal/poll/sendfile_unix.go

index 3f193e40a6dccdcf49a5d793ee7667925b574bb7..eaa48f39db405fc9e2fc759a7cd1c41bb073646b 100644 (file)
@@ -36,11 +36,9 @@ func SendFile(dstFD *FD, src int, size int64) (n int64, err error, handled bool)
                return sendFile(dstFD, src, nil, size)
        }
 
-       // Darwin/FreeBSD/DragonFly/Solaris's sendfile implementation
-       // doesn't use the current position of the file --
-       // if you pass it offset 0, it starts from offset 0.
-       // There's no way to tell it "start from current position",
-       // so we have to manage that explicitly.
+       // Non-Linux sendfile implementations don't use the current position of the source file,
+       // so we need to look up the position, pass it explicitly, and adjust it after
+       // sendfile returns.
        start, err := ignoringEINTR2(func() (int64, error) {
                return syscall.Seek(src, 0, io.SeekCurrent)
        })
@@ -48,36 +46,23 @@ func SendFile(dstFD *FD, src int, size int64) (n int64, err error, handled bool)
                return 0, err, false
        }
 
-       // Solaris requires us to pass a length to send,
-       // rather than accepting 0 as "send everything".
-       //
-       // Seek to the end of the source file to find its length.
-       //
-       // Important: If we ever remove this block
-       // (because Solaris has added a way to send everything, or we discovered a
-       // previously-unknown existing way),
-       // then some of the sendFile function will need updating.
-       //
-       // On Solaris, sendfile can return n>0 and EINVAL when successfully copying to a file.
-       // We ignore the EINVAL in this case.
-       //
-       // On non-Solaris platforms, when size==0 we call sendfile until it returns
-       // n==0 and success, indicating that it has copied the entire source file.
-       // If we were to do this on Solaris, then the final sendfile call could return (0, EINVAL),
-       // which we would treat as an error rather than successful completion of the copy.
-       // This never happens, because when size==0 on Solaris,
-       // we look up the actual file size here.
-       // If we change that, we need to handle the (0, EINVAL) case below.
        mustReposition := false
-       if runtime.GOOS == "solaris" && size == 0 {
-               end, err := ignoringEINTR2(func() (int64, error) {
-                       return syscall.Seek(src, 0, io.SeekEnd)
-               })
-               if err != nil {
-                       return 0, err, false
+       switch runtime.GOOS {
+       case "solaris", "illumos":
+               // Solaris/illumos requires us to pass a length to send,
+               // rather than accepting 0 as "send everything".
+               //
+               // Seek to the end of the source file to find its length.
+               if size == 0 {
+                       end, err := ignoringEINTR2(func() (int64, error) {
+                               return syscall.Seek(src, 0, io.SeekEnd)
+                       })
+                       if err != nil {
+                               return 0, err, false
+                       }
+                       size = end - start
+                       mustReposition = true
                }
-               size = end - start
-               mustReposition = true
        }
 
        pos := start
@@ -115,6 +100,22 @@ func sendFile(dstFD *FD, src int, offset *int64, size int64) (written int64, err
                if n > 0 {
                        written += int64(n)
                }
+
+               switch runtime.GOOS {
+               case "solaris", "illumos":
+                       // A quirk on Solaris/illumos: sendfile() claims to support out_fd
+                       // as a regular file but returns EINVAL when the out_fd
+                       // is not a socket of SOCK_STREAM, while it actually sends
+                       // out data anyway and updates the file offset.
+                       //
+                       // We ignore EINVAL if any sendfile call returned n > 0,
+                       // to handle the case where the last call returns 0 to indicate
+                       // no more data to send.
+                       if err == syscall.EINVAL && written > 0 {
+                               err = nil
+                       }
+               }
+
                switch err {
                case nil:
                        // We're done if sendfile copied no bytes
@@ -158,18 +159,11 @@ func sendFileChunk(dst, src int, offset *int64, size int) (n int, err error) {
        case "linux":
                // The offset is always nil on Linux.
                n, err = syscall.Sendfile(dst, src, offset, size)
-       case "solaris":
+       case "solaris", "illumos":
                // Trust the offset, not the return value from sendfile.
                start := *offset
                n, err = syscall.Sendfile(dst, src, offset, size)
                n = int(*offset - start)
-               // A quirk on Solaris: sendfile() claims to support out_fd
-               // as a regular file but returns EINVAL when the out_fd
-               // is not a socket of SOCK_STREAM, while it actually sends
-               // out data anyway and updates the file offset.
-               if err == syscall.EINVAL && n > 0 {
-                       err = nil
-               }
        default:
                start := *offset
                n, err = syscall.Sendfile(dst, src, offset, size)