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 5package poll 6 7import ( 8 "io" 9 "syscall" 10) 11 12// SendFile wraps the TransmitFile call. 13func SendFile(fd *FD, src syscall.Handle, n int64) (written int64, err error) { 14 defer func() { 15 TestHookDidSendFile(fd, 0, written, err, written > 0) 16 }() 17 if fd.kind == kindPipe { 18 // TransmitFile does not work with pipes 19 return 0, syscall.ESPIPE 20 } 21 if ft, _ := syscall.GetFileType(src); ft == syscall.FILE_TYPE_PIPE { 22 return 0, syscall.ESPIPE 23 } 24 25 if err := fd.writeLock(); err != nil { 26 return 0, err 27 } 28 defer fd.writeUnlock() 29 30 o := &fd.wop 31 o.handle = src 32 33 // TODO(brainman): skip calling syscall.Seek if OS allows it 34 curpos, err := syscall.Seek(o.handle, 0, io.SeekCurrent) 35 if err != nil { 36 return 0, err 37 } 38 39 if n <= 0 { // We don't know the size of the file so infer it. 40 // Find the number of bytes offset from curpos until the end of the file. 41 n, err = syscall.Seek(o.handle, -curpos, io.SeekEnd) 42 if err != nil { 43 return 44 } 45 // Now seek back to the original position. 46 if _, err = syscall.Seek(o.handle, curpos, io.SeekStart); err != nil { 47 return 48 } 49 } 50 51 // TransmitFile can be invoked in one call with at most 52 // 2,147,483,646 bytes: the maximum value for a 32-bit integer minus 1. 53 // See https://docs.microsoft.com/en-us/windows/win32/api/mswsock/nf-mswsock-transmitfile 54 const maxChunkSizePerCall = int64(0x7fffffff - 1) 55 56 for n > 0 { 57 chunkSize := maxChunkSizePerCall 58 if chunkSize > n { 59 chunkSize = n 60 } 61 62 o.qty = uint32(chunkSize) 63 o.o.Offset = uint32(curpos) 64 o.o.OffsetHigh = uint32(curpos >> 32) 65 66 nw, err := execIO(o, func(o *operation) error { 67 return syscall.TransmitFile(o.fd.Sysfd, o.handle, o.qty, 0, &o.o, nil, syscall.TF_WRITE_BEHIND) 68 }) 69 if err != nil { 70 return written, err 71 } 72 73 curpos += int64(nw) 74 75 // Some versions of Windows (Windows 10 1803) do not set 76 // file position after TransmitFile completes. 77 // So just use Seek to set file position. 78 if _, err = syscall.Seek(o.handle, curpos, io.SeekStart); err != nil { 79 return written, err 80 } 81 82 n -= int64(nw) 83 written += int64(nw) 84 } 85 86 return 87} 88