1// Copyright 2011 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
5// +build dragonfly freebsd
6
7package net
8
9import (
10	"internal/poll"
11	"io"
12	"os"
13)
14
15// sendFile copies the contents of r to c using the sendfile
16// system call to minimize copies.
17//
18// if handled == true, sendFile returns the number of bytes copied and any
19// non-EOF error.
20//
21// if handled == false, sendFile performed no work.
22func sendFile(c *netFD, r io.Reader) (written int64, err error, handled bool) {
23	// FreeBSD and DragonFly use 0 as the "until EOF" value.
24	// If you pass in more bytes than the file contains, it will
25	// loop back to the beginning ad nauseam until it's sent
26	// exactly the number of bytes told to. As such, we need to
27	// know exactly how many bytes to send.
28	var remain int64 = 0
29
30	lr, ok := r.(*io.LimitedReader)
31	if ok {
32		remain, r = lr.N, lr.R
33		if remain <= 0 {
34			return 0, nil, true
35		}
36	}
37	f, ok := r.(*os.File)
38	if !ok {
39		return 0, nil, false
40	}
41
42	if remain == 0 {
43		fi, err := f.Stat()
44		if err != nil {
45			return 0, err, false
46		}
47
48		remain = fi.Size()
49	}
50
51	// The other quirk with FreeBSD/DragonFly's sendfile
52	// implementation is that it doesn't use the current position
53	// of the file -- if you pass it offset 0, it starts from
54	// offset 0. There's no way to tell it "start from current
55	// position", so we have to manage that explicitly.
56	pos, err := f.Seek(0, io.SeekCurrent)
57	if err != nil {
58		return 0, err, false
59	}
60
61	written, err = poll.SendFile(&c.pfd, int(f.Fd()), pos, remain)
62
63	if lr != nil {
64		lr.N = remain - written
65	}
66	return written, wrapSyscallError("sendfile", err), written > 0
67}
68