-
Notifications
You must be signed in to change notification settings - Fork 4
/
sparse_windows.go
78 lines (65 loc) · 1.67 KB
/
sparse_windows.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
package sparsecat
import (
"errors"
"golang.org/x/sys/windows"
"io"
"os"
"syscall"
"unsafe"
)
const (
queryAllocRanges = 0x000940CF
setSparse = 0x000900c4
)
// detectDataSection detects the start and end of the next section containing data. This
// skips any sparse sections.
func detectDataSection(file *os.File, offset int64) (start int64, end int64, err error) {
// typedef struct _FILE_ALLOCATED_RANGE_BUFFER {
// LARGE_INTEGER FileOffset;
// LARGE_INTEGER Length;
//} FILE_ALLOCATED_RANGE_BUFFER, *PFILE_ALLOCATED_RANGE_BUFFER;
type allocRangeBuffer struct{ offset, length int64 }
// TODO: prevent this stat call
s, err := file.Stat()
if err != nil {
return 0, 0, err
}
queryRange := allocRangeBuffer{offset, s.Size()}
allocRanges := make([]allocRangeBuffer, 1)
var bytesReturned uint32
err = windows.DeviceIoControl(
windows.Handle(file.Fd()), queryAllocRanges,
(*byte)(unsafe.Pointer(&queryRange)), uint32(unsafe.Sizeof(queryRange)),
(*byte)(unsafe.Pointer(&allocRanges[0])), uint32(len(allocRanges)*int(unsafe.Sizeof(allocRanges[0]))),
&bytesReturned, nil,
)
if err != nil {
if !errors.Is(err, syscall.ERROR_MORE_DATA) {
panic(err)
}
}
// no error and nothing returned, assume EOF
if bytesReturned == 0 {
return 0, 0, io.EOF
}
return allocRanges[0].offset, allocRanges[0].offset + allocRanges[0].length, nil
}
func supportsSeekHole(f *os.File) bool {
return true
}
func SparseTruncate(file *os.File, size int64) error {
err := windows.DeviceIoControl(
windows.Handle(file.Fd()), setSparse,
nil, 0,
nil, 0,
nil, nil,
)
if err != nil {
return err
}
err = file.Truncate(size)
if err != nil {
return nil
}
return err
}