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 }