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//go:build (darwin && !ios) || dragonfly || freebsd || solaris 6 7package net 8 9import ( 10 "internal/poll" 11 "io" 12 "io/fs" 13 "syscall" 14) 15 16const supportsSendfile = true 17 18// sendFile copies the contents of r to c using the sendfile 19// system call to minimize copies. 20// 21// if handled == true, sendFile returns the number (potentially zero) of bytes 22// copied and any non-EOF error. 23// 24// if handled == false, sendFile performed no work. 25func sendFile(c *netFD, r io.Reader) (written int64, err error, handled bool) { 26 // Darwin, FreeBSD, DragonFly and Solaris use 0 as the "until EOF" value. 27 // If you pass in more bytes than the file contains, it will 28 // loop back to the beginning ad nauseam until it's sent 29 // exactly the number of bytes told to. As such, we need to 30 // know exactly how many bytes to send. 31 var remain int64 = 0 32 33 lr, ok := r.(*io.LimitedReader) 34 if ok { 35 remain, r = lr.N, lr.R 36 if remain <= 0 { 37 return 0, nil, true 38 } 39 } 40 // r might be an *os.File or an os.fileWithoutWriteTo. 41 // Type assert to an interface rather than *os.File directly to handle the latter case. 42 f, ok := r.(interface { 43 fs.File 44 io.Seeker 45 syscall.Conn 46 }) 47 if !ok { 48 return 0, nil, false 49 } 50 51 if remain == 0 { 52 fi, err := f.Stat() 53 if err != nil { 54 return 0, err, false 55 } 56 57 remain = fi.Size() 58 } 59 60 // The other quirk with Darwin/FreeBSD/DragonFly/Solaris's sendfile 61 // implementation is that it doesn't use the current position 62 // of the file -- if you pass it offset 0, it starts from 63 // offset 0. There's no way to tell it "start from current 64 // position", so we have to manage that explicitly. 65 pos, err := f.Seek(0, io.SeekCurrent) 66 if err != nil { 67 return 0, err, false 68 } 69 70 sc, err := f.SyscallConn() 71 if err != nil { 72 return 0, nil, false 73 } 74 75 var werr error 76 err = sc.Read(func(fd uintptr) bool { 77 written, werr, handled = poll.SendFile(&c.pfd, int(fd), pos, remain) 78 return true 79 }) 80 if err == nil { 81 err = werr 82 } 83 84 if lr != nil { 85 lr.N = remain - written 86 } 87 88 _, err1 := f.Seek(written, io.SeekCurrent) 89 if err1 != nil && err == nil { 90 return written, err1, handled 91 } 92 93 return written, wrapSyscallError("sendfile", err), handled 94} 95