You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
171 lines
5.0 KiB
171 lines
5.0 KiB
// +build windows |
|
|
|
package disk |
|
|
|
import ( |
|
"bytes" |
|
"context" |
|
"unsafe" |
|
|
|
"github.com/shirou/gopsutil/internal/common" |
|
"golang.org/x/sys/windows" |
|
) |
|
|
|
var ( |
|
procGetDiskFreeSpaceExW = common.Modkernel32.NewProc("GetDiskFreeSpaceExW") |
|
procGetLogicalDriveStringsW = common.Modkernel32.NewProc("GetLogicalDriveStringsW") |
|
procGetDriveType = common.Modkernel32.NewProc("GetDriveTypeW") |
|
provGetVolumeInformation = common.Modkernel32.NewProc("GetVolumeInformationW") |
|
) |
|
|
|
var ( |
|
FileFileCompression = int64(16) // 0x00000010 |
|
FileReadOnlyVolume = int64(524288) // 0x00080000 |
|
) |
|
|
|
type Win32_PerfFormattedData struct { |
|
Name string |
|
AvgDiskBytesPerRead uint64 |
|
AvgDiskBytesPerWrite uint64 |
|
AvgDiskReadQueueLength uint64 |
|
AvgDiskWriteQueueLength uint64 |
|
AvgDisksecPerRead uint64 |
|
AvgDisksecPerWrite uint64 |
|
} |
|
|
|
const WaitMSec = 500 |
|
|
|
func Usage(path string) (*UsageStat, error) { |
|
return UsageWithContext(context.Background(), path) |
|
} |
|
|
|
func UsageWithContext(ctx context.Context, path string) (*UsageStat, error) { |
|
ret := &UsageStat{} |
|
|
|
lpFreeBytesAvailable := int64(0) |
|
lpTotalNumberOfBytes := int64(0) |
|
lpTotalNumberOfFreeBytes := int64(0) |
|
diskret, _, err := procGetDiskFreeSpaceExW.Call( |
|
uintptr(unsafe.Pointer(windows.StringToUTF16Ptr(path))), |
|
uintptr(unsafe.Pointer(&lpFreeBytesAvailable)), |
|
uintptr(unsafe.Pointer(&lpTotalNumberOfBytes)), |
|
uintptr(unsafe.Pointer(&lpTotalNumberOfFreeBytes))) |
|
if diskret == 0 { |
|
return nil, err |
|
} |
|
ret = &UsageStat{ |
|
Path: path, |
|
Total: uint64(lpTotalNumberOfBytes), |
|
Free: uint64(lpTotalNumberOfFreeBytes), |
|
Used: uint64(lpTotalNumberOfBytes) - uint64(lpTotalNumberOfFreeBytes), |
|
UsedPercent: (float64(lpTotalNumberOfBytes) - float64(lpTotalNumberOfFreeBytes)) / float64(lpTotalNumberOfBytes) * 100, |
|
// InodesTotal: 0, |
|
// InodesFree: 0, |
|
// InodesUsed: 0, |
|
// InodesUsedPercent: 0, |
|
} |
|
return ret, nil |
|
} |
|
|
|
func Partitions(all bool) ([]PartitionStat, error) { |
|
return PartitionsWithContext(context.Background(), all) |
|
} |
|
|
|
func PartitionsWithContext(ctx context.Context, all bool) ([]PartitionStat, error) { |
|
var ret []PartitionStat |
|
lpBuffer := make([]byte, 254) |
|
diskret, _, err := procGetLogicalDriveStringsW.Call( |
|
uintptr(len(lpBuffer)), |
|
uintptr(unsafe.Pointer(&lpBuffer[0]))) |
|
if diskret == 0 { |
|
return ret, err |
|
} |
|
for _, v := range lpBuffer { |
|
if v >= 65 && v <= 90 { |
|
path := string(v) + ":" |
|
if path == "A:" || path == "B:" { // skip floppy drives |
|
continue |
|
} |
|
typepath, _ := windows.UTF16PtrFromString(path) |
|
typeret, _, _ := procGetDriveType.Call(uintptr(unsafe.Pointer(typepath))) |
|
if typeret == 0 { |
|
return ret, windows.GetLastError() |
|
} |
|
// 2: DRIVE_REMOVABLE 3: DRIVE_FIXED 4: DRIVE_REMOTE 5: DRIVE_CDROM |
|
|
|
if typeret == 2 || typeret == 3 || typeret == 4 || typeret == 5 { |
|
lpVolumeNameBuffer := make([]byte, 256) |
|
lpVolumeSerialNumber := int64(0) |
|
lpMaximumComponentLength := int64(0) |
|
lpFileSystemFlags := int64(0) |
|
lpFileSystemNameBuffer := make([]byte, 256) |
|
volpath, _ := windows.UTF16PtrFromString(string(v) + ":/") |
|
driveret, _, err := provGetVolumeInformation.Call( |
|
uintptr(unsafe.Pointer(volpath)), |
|
uintptr(unsafe.Pointer(&lpVolumeNameBuffer[0])), |
|
uintptr(len(lpVolumeNameBuffer)), |
|
uintptr(unsafe.Pointer(&lpVolumeSerialNumber)), |
|
uintptr(unsafe.Pointer(&lpMaximumComponentLength)), |
|
uintptr(unsafe.Pointer(&lpFileSystemFlags)), |
|
uintptr(unsafe.Pointer(&lpFileSystemNameBuffer[0])), |
|
uintptr(len(lpFileSystemNameBuffer))) |
|
if driveret == 0 { |
|
if typeret == 5 || typeret == 2 { |
|
continue //device is not ready will happen if there is no disk in the drive |
|
} |
|
return ret, err |
|
} |
|
opts := "rw" |
|
if lpFileSystemFlags&FileReadOnlyVolume != 0 { |
|
opts = "ro" |
|
} |
|
if lpFileSystemFlags&FileFileCompression != 0 { |
|
opts += ".compress" |
|
} |
|
|
|
d := PartitionStat{ |
|
Mountpoint: path, |
|
Device: path, |
|
Fstype: string(bytes.Replace(lpFileSystemNameBuffer, []byte("\x00"), []byte(""), -1)), |
|
Opts: opts, |
|
} |
|
ret = append(ret, d) |
|
} |
|
} |
|
} |
|
return ret, nil |
|
} |
|
|
|
func IOCounters(names ...string) (map[string]IOCountersStat, error) { |
|
return IOCountersWithContext(context.Background(), names...) |
|
} |
|
|
|
func IOCountersWithContext(ctx context.Context, names ...string) (map[string]IOCountersStat, error) { |
|
ret := make(map[string]IOCountersStat, 0) |
|
var dst []Win32_PerfFormattedData |
|
|
|
err := common.WMIQueryWithContext(ctx, "SELECT * FROM Win32_PerfFormattedData_PerfDisk_LogicalDisk", &dst) |
|
if err != nil { |
|
return ret, err |
|
} |
|
for _, d := range dst { |
|
if len(d.Name) > 3 { // not get _Total or Harddrive |
|
continue |
|
} |
|
|
|
if len(names) > 0 && !common.StringsHas(names, d.Name) { |
|
continue |
|
} |
|
|
|
ret[d.Name] = IOCountersStat{ |
|
Name: d.Name, |
|
ReadCount: uint64(d.AvgDiskReadQueueLength), |
|
WriteCount: d.AvgDiskWriteQueueLength, |
|
ReadBytes: uint64(d.AvgDiskBytesPerRead), |
|
WriteBytes: uint64(d.AvgDiskBytesPerWrite), |
|
ReadTime: d.AvgDisksecPerRead, |
|
WriteTime: d.AvgDisksecPerWrite, |
|
} |
|
} |
|
return ret, nil |
|
}
|
|
|