diff options
author | Emmanuel T Odeke <emmanuel@orijtech.com> | 2019-08-29 23:07:43 -0700 |
---|---|---|
committer | Emmanuel Odeke <emm.odeke@gmail.com> | 2019-09-07 08:46:41 +0000 |
commit | 3a067f71e9737fc8ae1e49348155d92d52e66718 (patch) | |
tree | fa52a5b66e9853a443f1b1490e2e186f048be9b3 /src | |
parent | 581526ce963f54b01eef95d2a76ecb6fc08ed91c (diff) | |
download | go-git-3a067f71e9737fc8ae1e49348155d92d52e66718.tar.gz |
net: handle >=2GiB files with sendfile on Windows
CL 187037 applied a fix to handle the case where
files larger than 2GiB were not being sendfile-d,
in one shot, rejecting any files whose size was
larger than the 2GiB.
This CL allows files that are larger than limit
by SendFile-ing in chunks of upto 2GiB per chunk.
The test has been excluded as testing with 3GB
requires creating a local file, flushing it
and then doing sendfile which takes a while
and could cause flakes on computers without capacity,
but the test can be retroactively accessed at:
https://go-review.googlesource.com/c/go/+/192518/8/src/net/sendfile_windows_test.go
Fixes #33193.
Change-Id: If57c25bc289aec82b748890ac1ac4f55798d6a5e
Reviewed-on: https://go-review.googlesource.com/c/go/+/192518
Run-TryBot: Emmanuel Odeke <emm.odeke@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Alex Brainman <alex.brainman@gmail.com>
Diffstat (limited to 'src')
-rw-r--r-- | src/net/sendfile_windows.go | 51 |
1 files changed, 38 insertions, 13 deletions
diff --git a/src/net/sendfile_windows.go b/src/net/sendfile_windows.go index a223e2562e..4e187205d9 100644 --- a/src/net/sendfile_windows.go +++ b/src/net/sendfile_windows.go @@ -18,10 +18,8 @@ import ( // non-EOF error. // // if handled == false, sendFile performed no work. -// -// Note that sendfile for windows does not support >2GB file. func sendFile(fd *netFD, r io.Reader) (written int64, err error, handled bool) { - var n int64 = 0 // by default, copy until EOF + var n int64 = 0 // by default, copy until EOF. lr, ok := r.(*io.LimitedReader) if ok { @@ -29,26 +27,53 @@ func sendFile(fd *netFD, r io.Reader) (written int64, err error, handled bool) { if n <= 0 { return 0, nil, true } - // TransmitFile can be invoked in one call with at most - // 2,147,483,646 bytes: the maximum value for a 32-bit integer minus 1. - // See https://docs.microsoft.com/en-us/windows/win32/api/mswsock/nf-mswsock-transmitfile - const maxSendBytes = 0x7fffffff - 1 - if n > maxSendBytes { - return 0, nil, false - } } + f, ok := r.(*os.File) if !ok { return 0, nil, false } - done, err := poll.SendFile(&fd.pfd, syscall.Handle(f.Fd()), n) + // TransmitFile can be invoked in one call with at most + // 2,147,483,646 bytes: the maximum value for a 32-bit integer minus 1. + // See https://docs.microsoft.com/en-us/windows/win32/api/mswsock/nf-mswsock-transmitfile + const maxChunkSizePerCall = int64(0x7fffffff - 1) + + switch { + case n <= maxChunkSizePerCall: + // The file is within sendfile's limits. + written, err = doSendFile(fd, lr, f, n) + + default: + // Now invoke doSendFile on the file in chunks of upto 2GiB per chunk. + for lr.N > 0 { // lr.N is decremented in every successful invocation of doSendFile. + chunkSize := maxChunkSizePerCall + if chunkSize > lr.N { + chunkSize = lr.N + } + var nw int64 + nw, err = doSendFile(fd, lr, f, chunkSize) + if err != nil { + break + } + written += nw + } + } + + // If any byte was copied, regardless of any error + // encountered mid-way, handled must be set to true. + return written, err, written > 0 +} +// doSendFile is a helper to invoke poll.SendFile. +// It will decrement lr.N by the number of written bytes. +func doSendFile(fd *netFD, lr *io.LimitedReader, f *os.File, remain int64) (written int64, err error) { + done, err := poll.SendFile(&fd.pfd, syscall.Handle(f.Fd()), remain) if err != nil { - return 0, wrapSyscallError("transmitfile", err), false + return 0, wrapSyscallError("transmitfile", err) } if lr != nil { lr.N -= int64(done) } - return int64(done), nil, true + return int64(done), nil } |