1
2
3 package disk
4
5 import (
6 "bytes"
7 "context"
8 "fmt"
9 "syscall"
10 "unsafe"
11
12 "github.com/shirou/gopsutil/internal/common"
13 "golang.org/x/sys/windows"
14 )
15
16 var (
17 procGetDiskFreeSpaceExW = common.Modkernel32.NewProc("GetDiskFreeSpaceExW")
18 procGetLogicalDriveStringsW = common.Modkernel32.NewProc("GetLogicalDriveStringsW")
19 procGetDriveType = common.Modkernel32.NewProc("GetDriveTypeW")
20 procGetVolumeInformation = common.Modkernel32.NewProc("GetVolumeInformationW")
21 )
22
23 var (
24 FileFileCompression = int64(16)
25 FileReadOnlyVolume = int64(524288)
26 )
27
28
29
30 type diskPerformance struct {
31 BytesRead int64
32 BytesWritten int64
33 ReadTime int64
34 WriteTime int64
35 IdleTime int64
36 ReadCount uint32
37 WriteCount uint32
38 QueueDepth uint32
39 SplitCount uint32
40 QueryTime int64
41 StorageDeviceNumber uint32
42 StorageManagerName [8]uint16
43 alignmentPadding uint32
44 }
45
46 func UsageWithContext(ctx context.Context, path string) (*UsageStat, error) {
47 lpFreeBytesAvailable := int64(0)
48 lpTotalNumberOfBytes := int64(0)
49 lpTotalNumberOfFreeBytes := int64(0)
50 diskret, _, err := procGetDiskFreeSpaceExW.Call(
51 uintptr(unsafe.Pointer(windows.StringToUTF16Ptr(path))),
52 uintptr(unsafe.Pointer(&lpFreeBytesAvailable)),
53 uintptr(unsafe.Pointer(&lpTotalNumberOfBytes)),
54 uintptr(unsafe.Pointer(&lpTotalNumberOfFreeBytes)))
55 if diskret == 0 {
56 return nil, err
57 }
58 ret := &UsageStat{
59 Path: path,
60 Total: uint64(lpTotalNumberOfBytes),
61 Free: uint64(lpTotalNumberOfFreeBytes),
62 Used: uint64(lpTotalNumberOfBytes) - uint64(lpTotalNumberOfFreeBytes),
63 UsedPercent: (float64(lpTotalNumberOfBytes) - float64(lpTotalNumberOfFreeBytes)) / float64(lpTotalNumberOfBytes) * 100,
64
65
66
67
68 }
69 return ret, nil
70 }
71
72 func PartitionsWithContext(ctx context.Context, all bool) ([]PartitionStat, error) {
73 var ret []PartitionStat
74 lpBuffer := make([]byte, 254)
75 diskret, _, err := procGetLogicalDriveStringsW.Call(
76 uintptr(len(lpBuffer)),
77 uintptr(unsafe.Pointer(&lpBuffer[0])))
78 if diskret == 0 {
79 return ret, err
80 }
81 for _, v := range lpBuffer {
82 if v >= 65 && v <= 90 {
83 path := string(v) + ":"
84 typepath, _ := windows.UTF16PtrFromString(path)
85 typeret, _, _ := procGetDriveType.Call(uintptr(unsafe.Pointer(typepath)))
86 if typeret == 0 {
87 return ret, windows.GetLastError()
88 }
89
90
91 if typeret == 2 || typeret == 3 || typeret == 4 || typeret == 5 {
92 lpVolumeNameBuffer := make([]byte, 256)
93 lpVolumeSerialNumber := int64(0)
94 lpMaximumComponentLength := int64(0)
95 lpFileSystemFlags := int64(0)
96 lpFileSystemNameBuffer := make([]byte, 256)
97 volpath, _ := windows.UTF16PtrFromString(string(v) + ":/")
98 driveret, _, err := procGetVolumeInformation.Call(
99 uintptr(unsafe.Pointer(volpath)),
100 uintptr(unsafe.Pointer(&lpVolumeNameBuffer[0])),
101 uintptr(len(lpVolumeNameBuffer)),
102 uintptr(unsafe.Pointer(&lpVolumeSerialNumber)),
103 uintptr(unsafe.Pointer(&lpMaximumComponentLength)),
104 uintptr(unsafe.Pointer(&lpFileSystemFlags)),
105 uintptr(unsafe.Pointer(&lpFileSystemNameBuffer[0])),
106 uintptr(len(lpFileSystemNameBuffer)))
107 if driveret == 0 {
108 if typeret == 5 || typeret == 2 {
109 continue
110 }
111 return ret, err
112 }
113 opts := "rw"
114 if lpFileSystemFlags&FileReadOnlyVolume != 0 {
115 opts = "ro"
116 }
117 if lpFileSystemFlags&FileFileCompression != 0 {
118 opts += ".compress"
119 }
120
121 d := PartitionStat{
122 Mountpoint: path,
123 Device: path,
124 Fstype: string(bytes.Replace(lpFileSystemNameBuffer, []byte("\x00"), []byte(""), -1)),
125 Opts: opts,
126 }
127 ret = append(ret, d)
128 }
129 }
130 }
131 return ret, nil
132 }
133
134 func IOCountersWithContext(ctx context.Context, names ...string) (map[string]IOCountersStat, error) {
135
136 drivemap := make(map[string]IOCountersStat, 0)
137 var diskPerformance diskPerformance
138
139 lpBuffer := make([]uint16, 254)
140 lpBufferLen, err := windows.GetLogicalDriveStrings(uint32(len(lpBuffer)), &lpBuffer[0])
141 if err != nil {
142 return drivemap, err
143 }
144 for _, v := range lpBuffer[:lpBufferLen] {
145 if 'A' <= v && v <= 'Z' {
146 path := string(rune(v)) + ":"
147 typepath, _ := windows.UTF16PtrFromString(path)
148 typeret := windows.GetDriveType(typepath)
149 if typeret == 0 {
150 return drivemap, windows.GetLastError()
151 }
152 if typeret != windows.DRIVE_FIXED {
153 continue
154 }
155 szDevice := fmt.Sprintf(`\\.\%s`, path)
156 const IOCTL_DISK_PERFORMANCE = 0x70020
157 h, err := windows.CreateFile(syscall.StringToUTF16Ptr(szDevice), 0, windows.FILE_SHARE_READ|windows.FILE_SHARE_WRITE, nil, windows.OPEN_EXISTING, 0, 0)
158 if err != nil {
159 if err == windows.ERROR_FILE_NOT_FOUND {
160 continue
161 }
162 return drivemap, err
163 }
164 defer windows.CloseHandle(h)
165
166 var diskPerformanceSize uint32
167 err = windows.DeviceIoControl(h, IOCTL_DISK_PERFORMANCE, nil, 0, (*byte)(unsafe.Pointer(&diskPerformance)), uint32(unsafe.Sizeof(diskPerformance)), &diskPerformanceSize, nil)
168 if err != nil {
169 return drivemap, err
170 }
171 drivemap[path] = IOCountersStat{
172 ReadBytes: uint64(diskPerformance.BytesRead),
173 WriteBytes: uint64(diskPerformance.BytesWritten),
174 ReadCount: uint64(diskPerformance.ReadCount),
175 WriteCount: uint64(diskPerformance.WriteCount),
176 ReadTime: uint64(diskPerformance.ReadTime / 10000 / 1000),
177 WriteTime: uint64(diskPerformance.WriteTime / 10000 / 1000),
178 Name: path,
179 }
180 }
181 }
182 return drivemap, nil
183 }
184
View as plain text