Skip to content

Commit 0fd414c

Browse files
neildgopherbot
authored andcommitted
internal/poll: handle (0, EINVAL) return from sendfile on Solaris
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 <[email protected]> Reviewed-by: Ian Lance Taylor <[email protected]> Commit-Queue: Ian Lance Taylor <[email protected]> Auto-Submit: Ian Lance Taylor <[email protected]>
1 parent 76a8409 commit 0fd414c

File tree

1 file changed

+35
-41
lines changed

1 file changed

+35
-41
lines changed

src/internal/poll/sendfile_unix.go

Lines changed: 35 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -36,48 +36,33 @@ func SendFile(dstFD *FD, src int, size int64) (n int64, err error, handled bool)
3636
return sendFile(dstFD, src, nil, size)
3737
}
3838

39-
// Darwin/FreeBSD/DragonFly/Solaris's sendfile implementation
40-
// doesn't use the current position of the file --
41-
// if you pass it offset 0, it starts from offset 0.
42-
// There's no way to tell it "start from current position",
43-
// so we have to manage that explicitly.
39+
// Non-Linux sendfile implementations don't use the current position of the source file,
40+
// so we need to look up the position, pass it explicitly, and adjust it after
41+
// sendfile returns.
4442
start, err := ignoringEINTR2(func() (int64, error) {
4543
return syscall.Seek(src, 0, io.SeekCurrent)
4644
})
4745
if err != nil {
4846
return 0, err, false
4947
}
5048

51-
// Solaris requires us to pass a length to send,
52-
// rather than accepting 0 as "send everything".
53-
//
54-
// Seek to the end of the source file to find its length.
55-
//
56-
// Important: If we ever remove this block
57-
// (because Solaris has added a way to send everything, or we discovered a
58-
// previously-unknown existing way),
59-
// then some of the sendFile function will need updating.
60-
//
61-
// On Solaris, sendfile can return n>0 and EINVAL when successfully copying to a file.
62-
// We ignore the EINVAL in this case.
63-
//
64-
// On non-Solaris platforms, when size==0 we call sendfile until it returns
65-
// n==0 and success, indicating that it has copied the entire source file.
66-
// If we were to do this on Solaris, then the final sendfile call could return (0, EINVAL),
67-
// which we would treat as an error rather than successful completion of the copy.
68-
// This never happens, because when size==0 on Solaris,
69-
// we look up the actual file size here.
70-
// If we change that, we need to handle the (0, EINVAL) case below.
7149
mustReposition := false
72-
if runtime.GOOS == "solaris" && size == 0 {
73-
end, err := ignoringEINTR2(func() (int64, error) {
74-
return syscall.Seek(src, 0, io.SeekEnd)
75-
})
76-
if err != nil {
77-
return 0, err, false
50+
switch runtime.GOOS {
51+
case "solaris", "illumos":
52+
// Solaris/illumos requires us to pass a length to send,
53+
// rather than accepting 0 as "send everything".
54+
//
55+
// Seek to the end of the source file to find its length.
56+
if size == 0 {
57+
end, err := ignoringEINTR2(func() (int64, error) {
58+
return syscall.Seek(src, 0, io.SeekEnd)
59+
})
60+
if err != nil {
61+
return 0, err, false
62+
}
63+
size = end - start
64+
mustReposition = true
7865
}
79-
size = end - start
80-
mustReposition = true
8166
}
8267

8368
pos := start
@@ -115,6 +100,22 @@ func sendFile(dstFD *FD, src int, offset *int64, size int64) (written int64, err
115100
if n > 0 {
116101
written += int64(n)
117102
}
103+
104+
switch runtime.GOOS {
105+
case "solaris", "illumos":
106+
// A quirk on Solaris/illumos: sendfile() claims to support out_fd
107+
// as a regular file but returns EINVAL when the out_fd
108+
// is not a socket of SOCK_STREAM, while it actually sends
109+
// out data anyway and updates the file offset.
110+
//
111+
// We ignore EINVAL if any sendfile call returned n > 0,
112+
// to handle the case where the last call returns 0 to indicate
113+
// no more data to send.
114+
if err == syscall.EINVAL && written > 0 {
115+
err = nil
116+
}
117+
}
118+
118119
switch err {
119120
case nil:
120121
// 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) {
158159
case "linux":
159160
// The offset is always nil on Linux.
160161
n, err = syscall.Sendfile(dst, src, offset, size)
161-
case "solaris":
162+
case "solaris", "illumos":
162163
// Trust the offset, not the return value from sendfile.
163164
start := *offset
164165
n, err = syscall.Sendfile(dst, src, offset, size)
165166
n = int(*offset - start)
166-
// A quirk on Solaris: sendfile() claims to support out_fd
167-
// as a regular file but returns EINVAL when the out_fd
168-
// is not a socket of SOCK_STREAM, while it actually sends
169-
// out data anyway and updates the file offset.
170-
if err == syscall.EINVAL && n > 0 {
171-
err = nil
172-
}
173167
default:
174168
start := *offset
175169
n, err = syscall.Sendfile(dst, src, offset, size)

0 commit comments

Comments
 (0)