Source File
sendfile_unix.go
Belonging Package
internal/poll
// Copyright 2024 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.//go:build darwin || dragonfly || freebsd || linux || solarispackage pollimport ()// SendFile wraps the sendfile system call.//// It copies data from src (a file descriptor) to dstFD,// starting at the current position of src.// It updates the current position of src to after the// copied data.//// If size is zero, it copies the rest of src.// Otherwise, it copies up to size bytes.//// The handled return parameter indicates whether SendFile// was able to handle some or all of the operation.// If handled is false, sendfile was unable to perform the copy,// has not modified the source or destination,// and the caller should perform the copy using a fallback implementation.func ( *FD, int, int64) ( int64, error, bool) {if := runtime.GOOS; == "linux" || == "android" {// Linux's sendfile doesn't require any setup:// It sends from the current position of the source file and// updates the position of the source after sending.return sendFile(, , nil, )}// 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., := ignoringEINTR2(func() (int64, error) {return syscall.Seek(, 0, io.SeekCurrent)})if != nil {return 0, , false}:=, , = sendFile(, , &, )if > 0 {ignoringEINTR2(func() (int64, error) {return syscall.Seek(, +, io.SeekStart)})}return , ,}// sendFile wraps the sendfile system call.func ( *FD, int, *int64, int64) ( int64, error, bool) {defer func() {TestHookDidSendFile(, , , , )}()if := .writeLock(); != nil {return 0, , false}defer .writeUnlock()if := .pd.prepareWrite(.isFile); != nil {return 0, , false}:= .Sysfdfor {// Some platforms support passing 0 to read to the end of the source,// but all platforms support just writing a large value.//// Limit the maximum size to fit in an int32, to avoid any possible overflow.:= 1<<31 - 1if > 0 {= int(min(-, int64()))}var int, = sendFileChunk(, , , , )if > 0 {+= int64()}switch {case nil:// We're done if sendfile copied no bytes// (we're at the end of the source)// or if we have a size limit and have reached it.//// If sendfile copied some bytes and we don't have a size limit,// try again to see if there is more data to copy.if == 0 || ( > 0 && >= ) {return , nil, true}case syscall.EAGAIN:// *BSD and Darwin can return EAGAIN with n > 0,// so check to see if the write has completed.// So far as we know all other platforms only// return EAGAIN when n == 0, but checking is harmless.if > 0 && >= {return , nil, true}if = .pd.waitWrite(.isFile); != nil {return , , true}case syscall.EINTR:// Retry.case syscall.ENOSYS, syscall.EOPNOTSUPP, syscall.EINVAL:// ENOSYS indicates no kernel support for sendfile.// EINVAL indicates a FD type that does not support sendfile.//// On Linux, copy_file_range can return EOPNOTSUPP when copying// to a NFS file (issue #40731); check for it here just in case.return , , > 0default:// We want to handle ENOTSUP like EOPNOTSUPP.// It's a pain to put it as a switch case// because on Linux systems ENOTSUP == EOPNOTSUPP,// so the compiler complains about a duplicate case.if == syscall.ENOTSUP {return , , > 0}// Not a retryable error.return , , true}}}func (, int, *int64, int, int64) ( int, error) {switch runtime.GOOS {case "linux", "android":// The offset is always nil on Linux., = syscall.Sendfile(, , , )case "solaris", "illumos":// Trust the offset, not the return value from sendfile.:= *, = syscall.Sendfile(, , , )= int(* - )// 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.//// Another quirk: sendfile transfers data and returns EINVAL when being// asked to transfer bytes more than the actual file size. For instance,// the source file is wrapped in an io.LimitedReader with larger size// than the actual file size.//// To handle these cases we ignore EINVAL if any call to sendfile was// able to send data.if == syscall.EINVAL && ( > 0 || > 0) {= nil}default::= *, = syscall.Sendfile(, , , )if > 0 {// The BSD implementations of syscall.Sendfile don't// update the offset parameter (despite it being a *int64).//// Trust the return value from sendfile, not the offset.* = + int64()}}return}
The pages are generated with Golds v0.7.6. (GOOS=linux GOARCH=amd64)