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 5 package net 6 7 import ( 8 "io" 9 "os" 10 "syscall" 11 ) 12 13 // Not strictly needed, but very helpful for debugging, see issue #10221. 14 //go:cgo_import_dynamic _ _ "libsendfile.so" 15 //go:cgo_import_dynamic _ _ "libsocket.so" 16 17 // maxSendfileSize is the largest chunk size we ask the kernel to copy 18 // at a time. 19 const maxSendfileSize int = 4 << 20 20 21 // sendFile copies the contents of r to c using the sendfile 22 // system call to minimize copies. 23 // 24 // if handled == true, sendFile returns the number of bytes copied and any 25 // non-EOF error. 26 // 27 // if handled == false, sendFile performed no work. 28 func sendFile(c *netFD, r io.Reader) (written int64, err error, handled bool) { 29 // Solaris uses 0 as the "until EOF" value. If you pass in more bytes than the 30 // file contains, it will loop back to the beginning ad nauseam until it's sent 31 // exactly the number of bytes told to. As such, we need to know exactly how many 32 // bytes to send. 33 var remain int64 = 0 34 35 lr, ok := r.(*io.LimitedReader) 36 if ok { 37 remain, r = lr.N, lr.R 38 if remain <= 0 { 39 return 0, nil, true 40 } 41 } 42 f, ok := r.(*os.File) 43 if !ok { 44 return 0, nil, false 45 } 46 47 if remain == 0 { 48 fi, err := f.Stat() 49 if err != nil { 50 return 0, err, false 51 } 52 53 remain = fi.Size() 54 } 55 56 // The other quirk with Solaris's sendfile implementation is that it doesn't 57 // use the current position of the file -- if you pass it offset 0, it starts 58 // from offset 0. There's no way to tell it "start from current position", so 59 // we have to manage that explicitly. 60 pos, err := f.Seek(0, os.SEEK_CUR) 61 if err != nil { 62 return 0, err, false 63 } 64 65 if err := c.writeLock(); err != nil { 66 return 0, err, true 67 } 68 defer c.writeUnlock() 69 70 dst := c.sysfd 71 src := int(f.Fd()) 72 for remain > 0 { 73 n := maxSendfileSize 74 if int64(n) > remain { 75 n = int(remain) 76 } 77 pos1 := pos 78 n, err1 := syscall.Sendfile(dst, src, &pos1, n) 79 if n > 0 { 80 pos += int64(n) 81 written += int64(n) 82 remain -= int64(n) 83 } 84 if n == 0 && err1 == nil { 85 break 86 } 87 if err1 == syscall.EAGAIN { 88 if err1 = c.pd.WaitWrite(); err1 == nil { 89 continue 90 } 91 } 92 if err1 == syscall.EINTR { 93 continue 94 } 95 if err1 != nil { 96 // This includes syscall.ENOSYS (no kernel 97 // support) and syscall.EINVAL (fd types which 98 // don't implement sendfile) 99 err = err1 100 break 101 } 102 } 103 if lr != nil { 104 lr.N = remain 105 } 106 if err != nil { 107 err = os.NewSyscallError("sendfile", err) 108 } 109 return written, err, written > 0 110 } 111