Source file src/internal/poll/sendfile_windows.go

     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  package poll
     6  
     7  import (
     8  	"io"
     9  	"syscall"
    10  )
    11  
    12  // SendFile wraps the TransmitFile call.
    13  func SendFile(fd *FD, src uintptr, size int64) (written int64, err error, handled bool) {
    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, false
    20  	}
    21  	hsrc := syscall.Handle(src)
    22  	if ft, _ := syscall.GetFileType(hsrc); ft == syscall.FILE_TYPE_PIPE {
    23  		return 0, syscall.ESPIPE, false
    24  	}
    25  
    26  	if err := fd.writeLock(); err != nil {
    27  		return 0, err, false
    28  	}
    29  	defer fd.writeUnlock()
    30  
    31  	// Get the file size so we don't read past the end of the file.
    32  	var fi syscall.ByHandleFileInformation
    33  	if err := syscall.GetFileInformationByHandle(hsrc, &fi); err != nil {
    34  		return 0, err, false
    35  	}
    36  	fileSize := int64(fi.FileSizeHigh)<<32 + int64(fi.FileSizeLow)
    37  	startpos, err := syscall.Seek(hsrc, 0, io.SeekCurrent)
    38  	if err != nil {
    39  		return 0, err, false
    40  	}
    41  	maxSize := fileSize - startpos
    42  	if size <= 0 {
    43  		size = maxSize
    44  	} else {
    45  		size = min(size, maxSize)
    46  	}
    47  
    48  	defer func() {
    49  		if written > 0 {
    50  			// Some versions of Windows (Windows 10 1803) do not set
    51  			// file position after TransmitFile completes.
    52  			// So just use Seek to set file position.
    53  			_, serr := syscall.Seek(hsrc, startpos+written, io.SeekStart)
    54  			if err != nil {
    55  				err = serr
    56  			}
    57  		}
    58  	}()
    59  
    60  	// TransmitFile can be invoked in one call with at most
    61  	// 2,147,483,646 bytes: the maximum value for a 32-bit integer minus 1.
    62  	// See https://docs.microsoft.com/en-us/windows/win32/api/mswsock/nf-mswsock-transmitfile
    63  	const maxChunkSizePerCall = int64(0x7fffffff - 1)
    64  
    65  	o := &fd.wop
    66  	o.handle = hsrc
    67  	for size > 0 {
    68  		chunkSize := maxChunkSizePerCall
    69  		if chunkSize > size {
    70  			chunkSize = size
    71  		}
    72  
    73  		off := startpos + written
    74  		o.o.Offset = uint32(off)
    75  		o.o.OffsetHigh = uint32(off >> 32)
    76  
    77  		n, err := execIO(o, func(o *operation) error {
    78  			o.qty = uint32(chunkSize)
    79  			return syscall.TransmitFile(o.fd.Sysfd, o.handle, o.qty, 0, &o.o, nil, syscall.TF_WRITE_BEHIND)
    80  		})
    81  		if err != nil {
    82  			return written, err, written > 0
    83  		}
    84  
    85  		size -= int64(n)
    86  		written += int64(n)
    87  	}
    88  
    89  	return written, nil, written > 0
    90  }
    91  

View as plain text