github.com/ncw/rclone@v1.48.1-0.20190724201158-a35aa1360e3e/backend/local/preallocate_windows.go (about)

     1  //+build windows
     2  
     3  package local
     4  
     5  import (
     6  	"os"
     7  	"syscall"
     8  	"unsafe"
     9  
    10  	"github.com/pkg/errors"
    11  	"golang.org/x/sys/windows"
    12  )
    13  
    14  var (
    15  	ntdll                        = windows.NewLazySystemDLL("ntdll.dll")
    16  	ntQueryVolumeInformationFile = ntdll.NewProc("NtQueryVolumeInformationFile")
    17  	ntSetInformationFile         = ntdll.NewProc("NtSetInformationFile")
    18  )
    19  
    20  type fileAllocationInformation struct {
    21  	AllocationSize uint64
    22  }
    23  
    24  type fileFsSizeInformation struct {
    25  	TotalAllocationUnits     uint64
    26  	AvailableAllocationUnits uint64
    27  	SectorsPerAllocationUnit uint32
    28  	BytesPerSector           uint32
    29  }
    30  
    31  type ioStatusBlock struct {
    32  	Status, Information uintptr
    33  }
    34  
    35  // preAllocate the file for performance reasons
    36  func preAllocate(size int64, out *os.File) error {
    37  	if size <= 0 {
    38  		return nil
    39  	}
    40  
    41  	var (
    42  		iosb       ioStatusBlock
    43  		fsSizeInfo fileFsSizeInformation
    44  		allocInfo  fileAllocationInformation
    45  	)
    46  
    47  	// Query info about the block sizes on the file system
    48  	_, _, e1 := ntQueryVolumeInformationFile.Call(
    49  		uintptr(out.Fd()),
    50  		uintptr(unsafe.Pointer(&iosb)),
    51  		uintptr(unsafe.Pointer(&fsSizeInfo)),
    52  		uintptr(unsafe.Sizeof(fsSizeInfo)),
    53  		uintptr(3), // FileFsSizeInformation
    54  	)
    55  	if e1 != nil && e1 != syscall.Errno(0) {
    56  		return errors.Wrap(e1, "preAllocate NtQueryVolumeInformationFile failed")
    57  	}
    58  
    59  	// Calculate the allocation size
    60  	clusterSize := uint64(fsSizeInfo.BytesPerSector) * uint64(fsSizeInfo.SectorsPerAllocationUnit)
    61  	if clusterSize <= 0 {
    62  		return errors.Errorf("preAllocate clusterSize %d <= 0", clusterSize)
    63  	}
    64  	allocInfo.AllocationSize = (1 + uint64(size-1)/clusterSize) * clusterSize
    65  
    66  	// Ask for the allocation
    67  	_, _, e1 = ntSetInformationFile.Call(
    68  		uintptr(out.Fd()),
    69  		uintptr(unsafe.Pointer(&iosb)),
    70  		uintptr(unsafe.Pointer(&allocInfo)),
    71  		uintptr(unsafe.Sizeof(allocInfo)),
    72  		uintptr(19), // FileAllocationInformation
    73  	)
    74  	if e1 != nil && e1 != syscall.Errno(0) {
    75  		return errors.Wrap(e1, "preAllocate NtSetInformationFile failed")
    76  	}
    77  
    78  	return nil
    79  }