github.com/10XDev/rclone@v1.52.3-0.20200626220027-16af9ab76b2a/lib/file/preallocate_windows.go (about) 1 //+build windows 2 3 package file 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 // PreallocateImplemented is a constant indicating whether the 36 // implementation of Preallocate actually does anything. 37 const PreallocateImplemented = true 38 39 // PreAllocate the file for performance reasons 40 func PreAllocate(size int64, out *os.File) error { 41 if size <= 0 { 42 return nil 43 } 44 45 var ( 46 iosb ioStatusBlock 47 fsSizeInfo fileFsSizeInformation 48 allocInfo fileAllocationInformation 49 ) 50 51 // Query info about the block sizes on the file system 52 _, _, e1 := ntQueryVolumeInformationFile.Call( 53 uintptr(out.Fd()), 54 uintptr(unsafe.Pointer(&iosb)), 55 uintptr(unsafe.Pointer(&fsSizeInfo)), 56 uintptr(unsafe.Sizeof(fsSizeInfo)), 57 uintptr(3), // FileFsSizeInformation 58 ) 59 if e1 != nil && e1 != syscall.Errno(0) { 60 return errors.Wrap(e1, "preAllocate NtQueryVolumeInformationFile failed") 61 } 62 63 // Calculate the allocation size 64 clusterSize := uint64(fsSizeInfo.BytesPerSector) * uint64(fsSizeInfo.SectorsPerAllocationUnit) 65 if clusterSize <= 0 { 66 return errors.Errorf("preAllocate clusterSize %d <= 0", clusterSize) 67 } 68 allocInfo.AllocationSize = (1 + uint64(size-1)/clusterSize) * clusterSize 69 70 // Ask for the allocation 71 _, _, e1 = ntSetInformationFile.Call( 72 uintptr(out.Fd()), 73 uintptr(unsafe.Pointer(&iosb)), 74 uintptr(unsafe.Pointer(&allocInfo)), 75 uintptr(unsafe.Sizeof(allocInfo)), 76 uintptr(19), // FileAllocationInformation 77 ) 78 if e1 != nil && e1 != syscall.Errno(0) { 79 return errors.Wrap(e1, "preAllocate NtSetInformationFile failed") 80 } 81 82 return nil 83 } 84 85 const ( 86 FSCTL_SET_SPARSE = 0x000900c4 87 ) 88 89 // SetSparseImplemented is a constant indicating whether the 90 // implementation of SetSparse actually does anything. 91 const SetSparseImplemented = true 92 93 // SetSparse makes the file be a sparse file 94 func SetSparse(out *os.File) error { 95 var bytesReturned uint32 96 err := syscall.DeviceIoControl(syscall.Handle(out.Fd()), FSCTL_SET_SPARSE, nil, 0, nil, 0, &bytesReturned, nil) 97 if err != nil { 98 return errors.Wrap(err, "DeviceIoControl FSCTL_SET_SPARSE") 99 } 100 return nil 101 }