2018-08-19 12:38:26 +02:00
|
|
|
//+build windows
|
|
|
|
|
|
|
|
package local
|
|
|
|
|
|
|
|
import (
|
|
|
|
"os"
|
|
|
|
"syscall"
|
|
|
|
"unsafe"
|
|
|
|
|
|
|
|
"github.com/pkg/errors"
|
|
|
|
"golang.org/x/sys/windows"
|
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
ntdll = windows.NewLazySystemDLL("ntdll.dll")
|
|
|
|
ntQueryVolumeInformationFile = ntdll.NewProc("NtQueryVolumeInformationFile")
|
|
|
|
ntSetInformationFile = ntdll.NewProc("NtSetInformationFile")
|
|
|
|
)
|
|
|
|
|
|
|
|
type fileAllocationInformation struct {
|
|
|
|
AllocationSize uint64
|
|
|
|
}
|
|
|
|
|
|
|
|
type fileFsSizeInformation struct {
|
|
|
|
TotalAllocationUnits uint64
|
|
|
|
AvailableAllocationUnits uint64
|
|
|
|
SectorsPerAllocationUnit uint32
|
|
|
|
BytesPerSector uint32
|
|
|
|
}
|
|
|
|
|
|
|
|
type ioStatusBlock struct {
|
|
|
|
Status, Information uintptr
|
|
|
|
}
|
|
|
|
|
|
|
|
// preAllocate the file for performance reasons
|
|
|
|
func preAllocate(size int64, out *os.File) error {
|
|
|
|
if size <= 0 {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
var (
|
|
|
|
iosb ioStatusBlock
|
|
|
|
fsSizeInfo fileFsSizeInformation
|
|
|
|
allocInfo fileAllocationInformation
|
|
|
|
)
|
|
|
|
|
|
|
|
// Query info about the block sizes on the file system
|
|
|
|
_, _, e1 := ntQueryVolumeInformationFile.Call(
|
|
|
|
uintptr(out.Fd()),
|
|
|
|
uintptr(unsafe.Pointer(&iosb)),
|
|
|
|
uintptr(unsafe.Pointer(&fsSizeInfo)),
|
|
|
|
uintptr(unsafe.Sizeof(fsSizeInfo)),
|
|
|
|
uintptr(3), // FileFsSizeInformation
|
|
|
|
)
|
|
|
|
if e1 != nil && e1 != syscall.Errno(0) {
|
|
|
|
return errors.Wrap(e1, "preAllocate NtQueryVolumeInformationFile failed")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Calculate the allocation size
|
|
|
|
clusterSize := uint64(fsSizeInfo.BytesPerSector) * uint64(fsSizeInfo.SectorsPerAllocationUnit)
|
|
|
|
if clusterSize <= 0 {
|
|
|
|
return errors.Errorf("preAllocate clusterSize %d <= 0", clusterSize)
|
|
|
|
}
|
|
|
|
allocInfo.AllocationSize = (1 + uint64(size-1)/clusterSize) * clusterSize
|
|
|
|
|
|
|
|
// Ask for the allocation
|
|
|
|
_, _, e1 = ntSetInformationFile.Call(
|
|
|
|
uintptr(out.Fd()),
|
|
|
|
uintptr(unsafe.Pointer(&iosb)),
|
|
|
|
uintptr(unsafe.Pointer(&allocInfo)),
|
|
|
|
uintptr(unsafe.Sizeof(allocInfo)),
|
|
|
|
uintptr(19), // FileAllocationInformation
|
|
|
|
)
|
|
|
|
if e1 != nil && e1 != syscall.Errno(0) {
|
|
|
|
return errors.Wrap(e1, "preAllocate NtSetInformationFile failed")
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
2020-03-06 13:41:48 +01:00
|
|
|
|
|
|
|
const (
|
|
|
|
FSCTL_SET_SPARSE = 0x000900c4
|
|
|
|
)
|
|
|
|
|
|
|
|
// setSparse makes the file be a sparse file
|
|
|
|
func setSparse(out *os.File) error {
|
|
|
|
err := syscall.DeviceIoControl(syscall.Handle(out.Fd()), FSCTL_SET_SPARSE, nil, 0, nil, 0, nil, nil)
|
|
|
|
if err != nil {
|
|
|
|
return errors.Wrap(err, "DeviceIoControl FSCTL_SET_SPARSE")
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|