1// Copyright 2015 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package poll
6
7import "syscall"
8
9// Not strictly needed, but very helpful for debugging, see issue #10221.
10//go:cgo_import_dynamic _ _ "libsendfile.so"
11//go:cgo_import_dynamic _ _ "libsocket.so"
12
13// maxSendfileSize is the largest chunk size we ask the kernel to copy
14// at a time.
15const maxSendfileSize int = 4 << 20
16
17// SendFile wraps the sendfile system call.
18func SendFile(dstFD *FD, src int, pos, remain int64) (int64, error) {
19	if err := dstFD.writeLock(); err != nil {
20		return 0, err
21	}
22	defer dstFD.writeUnlock()
23	if err := dstFD.pd.prepareWrite(dstFD.isFile); err != nil {
24		return 0, err
25	}
26
27	dst := int(dstFD.Sysfd)
28	var written int64
29	var err error
30	for remain > 0 {
31		n := maxSendfileSize
32		if int64(n) > remain {
33			n = int(remain)
34		}
35		pos1 := pos
36		n, err1 := syscall.Sendfile(dst, src, &pos1, n)
37		if err1 == syscall.EAGAIN || err1 == syscall.EINTR {
38			// partial write may have occurred
39			n = int(pos1 - pos)
40		}
41		if n > 0 {
42			pos += int64(n)
43			written += int64(n)
44			remain -= int64(n)
45		} else if n == 0 && err1 == nil {
46			break
47		}
48		if err1 == syscall.EAGAIN {
49			if err1 = dstFD.pd.waitWrite(dstFD.isFile); err1 == nil {
50				continue
51			}
52		}
53		if err1 == syscall.EINTR {
54			continue
55		}
56		if err1 != nil {
57			// This includes syscall.ENOSYS (no kernel
58			// support) and syscall.EINVAL (fd types which
59			// don't implement sendfile)
60			err = err1
61			break
62		}
63	}
64	return written, err
65}
66