1// +build windows
2
3package disk
4
5import (
6	"bytes"
7	"context"
8	"unsafe"
9
10	"github.com/shirou/gopsutil/internal/common"
11	"golang.org/x/sys/windows"
12)
13
14var (
15	procGetDiskFreeSpaceExW     = common.Modkernel32.NewProc("GetDiskFreeSpaceExW")
16	procGetLogicalDriveStringsW = common.Modkernel32.NewProc("GetLogicalDriveStringsW")
17	procGetDriveType            = common.Modkernel32.NewProc("GetDriveTypeW")
18	procGetVolumeInformation    = common.Modkernel32.NewProc("GetVolumeInformationW")
19)
20
21var (
22	FileFileCompression = int64(16)     // 0x00000010
23	FileReadOnlyVolume  = int64(524288) // 0x00080000
24)
25
26type Win32_PerfFormattedData struct {
27	Name                    string
28	AvgDiskBytesPerRead     uint64
29	AvgDiskBytesPerWrite    uint64
30	AvgDiskReadQueueLength  uint64
31	AvgDiskWriteQueueLength uint64
32	AvgDisksecPerRead       uint64
33	AvgDisksecPerWrite      uint64
34}
35
36const WaitMSec = 500
37
38func Usage(path string) (*UsageStat, error) {
39	return UsageWithContext(context.Background(), path)
40}
41
42func UsageWithContext(ctx context.Context, path string) (*UsageStat, error) {
43	lpFreeBytesAvailable := int64(0)
44	lpTotalNumberOfBytes := int64(0)
45	lpTotalNumberOfFreeBytes := int64(0)
46	diskret, _, err := procGetDiskFreeSpaceExW.Call(
47		uintptr(unsafe.Pointer(windows.StringToUTF16Ptr(path))),
48		uintptr(unsafe.Pointer(&lpFreeBytesAvailable)),
49		uintptr(unsafe.Pointer(&lpTotalNumberOfBytes)),
50		uintptr(unsafe.Pointer(&lpTotalNumberOfFreeBytes)))
51	if diskret == 0 {
52		return nil, err
53	}
54	ret := &UsageStat{
55		Path:        path,
56		Total:       uint64(lpTotalNumberOfBytes),
57		Free:        uint64(lpTotalNumberOfFreeBytes),
58		Used:        uint64(lpTotalNumberOfBytes) - uint64(lpTotalNumberOfFreeBytes),
59		UsedPercent: (float64(lpTotalNumberOfBytes) - float64(lpTotalNumberOfFreeBytes)) / float64(lpTotalNumberOfBytes) * 100,
60		// InodesTotal: 0,
61		// InodesFree: 0,
62		// InodesUsed: 0,
63		// InodesUsedPercent: 0,
64	}
65	return ret, nil
66}
67
68func Partitions(all bool) ([]PartitionStat, error) {
69	return PartitionsWithContext(context.Background(), all)
70}
71
72func 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			// 2: DRIVE_REMOVABLE 3: DRIVE_FIXED 4: DRIVE_REMOTE 5: DRIVE_CDROM
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 //device is not ready will happen if there is no disk in the drive
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
134func IOCounters(names ...string) (map[string]IOCountersStat, error) {
135	return IOCountersWithContext(context.Background(), names...)
136}
137
138func IOCountersWithContext(ctx context.Context, names ...string) (map[string]IOCountersStat, error) {
139	ret := make(map[string]IOCountersStat, 0)
140	var dst []Win32_PerfFormattedData
141
142	err := common.WMIQueryWithContext(ctx, "SELECT * FROM Win32_PerfFormattedData_PerfDisk_LogicalDisk", &dst)
143	if err != nil {
144		return ret, err
145	}
146	for _, d := range dst {
147		if len(d.Name) > 3 { // not get _Total or Harddrive
148			continue
149		}
150
151		if len(names) > 0 && !common.StringsHas(names, d.Name) {
152			continue
153		}
154
155		ret[d.Name] = IOCountersStat{
156			Name:       d.Name,
157			ReadCount:  uint64(d.AvgDiskReadQueueLength),
158			WriteCount: d.AvgDiskWriteQueueLength,
159			ReadBytes:  uint64(d.AvgDiskBytesPerRead),
160			WriteBytes: uint64(d.AvgDiskBytesPerWrite),
161			ReadTime:   d.AvgDisksecPerRead,
162			WriteTime:  d.AvgDisksecPerWrite,
163		}
164	}
165	return ret, nil
166}
167