blob: 7a2b48c6cfa46774f4e7295914b901645efc4903 [file] [log] [blame]
Brad Fitzpatrick51947442016-03-01 22:57:46 +00001// Copyright 2011 The Go Authors. All rights reserved.
L Campbella9a8d7b2012-06-25 20:26:19 -04002// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
Ian Lance Taylor27520cc2017-02-08 15:03:56 -08005// +build dragonfly freebsd
6
L Campbella9a8d7b2012-06-25 20:26:19 -04007package net
8
9import (
Ian Lance Taylor3792db52017-02-10 14:59:38 -080010 "internal/poll"
L Campbella9a8d7b2012-06-25 20:26:19 -040011 "io"
12 "os"
L Campbella9a8d7b2012-06-25 20:26:19 -040013)
14
L Campbella9a8d7b2012-06-25 20:26:19 -040015// 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) {
Ian Lance Taylor27520cc2017-02-08 15:03:56 -080023 // 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.
L Campbella9a8d7b2012-06-25 20:26:19 -040028 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
Ian Lance Taylor27520cc2017-02-08 15:03:56 -080051 // 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.
Brad Fitzpatrick381e5ee2016-04-13 04:35:37 +000056 pos, err := f.Seek(0, io.SeekCurrent)
L Campbella9a8d7b2012-06-25 20:26:19 -040057 if err != nil {
58 return 0, err, false
59 }
60
Ian Lance Taylor3792db52017-02-10 14:59:38 -080061 written, err = poll.SendFile(&c.pfd, int(f.Fd()), pos, remain)
L Campbella9a8d7b2012-06-25 20:26:19 -040062
L Campbella9a8d7b2012-06-25 20:26:19 -040063 if lr != nil {
Ian Lance Taylor3792db52017-02-10 14:59:38 -080064 lr.N = remain - written
L Campbella9a8d7b2012-06-25 20:26:19 -040065 }
Ian Lance Taylor3792db52017-02-10 14:59:38 -080066 return written, wrapSyscallError("sendfile", err), written > 0
L Campbella9a8d7b2012-06-25 20:26:19 -040067}