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