2014-04-18 21:28:00 +09:00
// +build windows
2014-12-30 22:09:05 +09:00
package disk
2014-04-18 21:28:00 +09:00
2014-04-20 00:03:47 +09:00
import (
2014-04-20 01:53:34 +09:00
"bytes"
2017-11-09 15:44:26 -08:00
"context"
2014-04-20 00:03:47 +09:00
"unsafe"
2014-11-27 10:18:15 +09:00
2015-10-20 00:04:57 +09:00
"github.com/shirou/gopsutil/internal/common"
2017-06-02 13:51:00 -07:00
"golang.org/x/sys/windows"
2014-04-20 00:03:47 +09:00
)
var (
2014-11-27 22:28:05 +09:00
procGetDiskFreeSpaceExW = common . Modkernel32 . NewProc ( "GetDiskFreeSpaceExW" )
procGetLogicalDriveStringsW = common . Modkernel32 . NewProc ( "GetLogicalDriveStringsW" )
procGetDriveType = common . Modkernel32 . NewProc ( "GetDriveTypeW" )
provGetVolumeInformation = common . Modkernel32 . NewProc ( "GetVolumeInformationW" )
2014-04-20 01:53:34 +09:00
)
var (
2014-08-15 20:05:26 +02:00
FileFileCompression = int64 ( 16 ) // 0x00000010
FileReadOnlyVolume = int64 ( 524288 ) // 0x00080000
2014-04-20 00:03:47 +09:00
)
2014-04-18 21:28:00 +09:00
2015-09-05 22:53:18 +09:00
type Win32_PerfFormattedData struct {
Name string
AvgDiskBytesPerRead uint64
AvgDiskBytesPerWrite uint64
AvgDiskReadQueueLength uint64
AvgDiskWriteQueueLength uint64
AvgDisksecPerRead uint64
AvgDisksecPerWrite uint64
}
2018-06-22 09:34:39 +08:00
type Win32_DiskDrive struct {
DeviceID string
FirmwareRevision string
InterfaceType string
SerialNumber string
}
type Win32_DiskPartition struct {
DeviceID string
}
2015-09-05 22:53:18 +09:00
2015-02-19 23:52:06 +09:00
const WaitMSec = 500
2016-03-22 23:09:12 +09:00
func Usage ( path string ) ( * UsageStat , error ) {
2017-12-31 15:25:49 +09:00
return UsageWithContext ( context . Background ( ) , path )
}
func UsageWithContext ( ctx context . Context , path string ) ( * UsageStat , error ) {
2016-03-22 23:09:12 +09:00
ret := & UsageStat { }
2014-04-18 21:28:00 +09:00
2014-04-20 00:03:47 +09:00
lpFreeBytesAvailable := int64 ( 0 )
lpTotalNumberOfBytes := int64 ( 0 )
lpTotalNumberOfFreeBytes := int64 ( 0 )
2014-04-20 01:53:34 +09:00
diskret , _ , err := procGetDiskFreeSpaceExW . Call (
2017-06-02 13:51:00 -07:00
uintptr ( unsafe . Pointer ( windows . StringToUTF16Ptr ( path ) ) ) ,
2014-04-20 00:03:47 +09:00
uintptr ( unsafe . Pointer ( & lpFreeBytesAvailable ) ) ,
uintptr ( unsafe . Pointer ( & lpTotalNumberOfBytes ) ) ,
uintptr ( unsafe . Pointer ( & lpTotalNumberOfFreeBytes ) ) )
if diskret == 0 {
2015-08-27 09:51:03 -06:00
return nil , err
}
2016-03-22 23:09:12 +09:00
ret = & UsageStat {
2015-09-04 11:51:34 -06:00
Path : path ,
Total : uint64 ( lpTotalNumberOfBytes ) ,
Free : uint64 ( lpTotalNumberOfFreeBytes ) ,
Used : uint64 ( lpTotalNumberOfBytes ) - uint64 ( lpTotalNumberOfFreeBytes ) ,
UsedPercent : ( float64 ( lpTotalNumberOfBytes ) - float64 ( lpTotalNumberOfFreeBytes ) ) / float64 ( lpTotalNumberOfBytes ) * 100 ,
2015-08-27 09:51:03 -06:00
// InodesTotal: 0,
// InodesFree: 0,
// InodesUsed: 0,
// InodesUsedPercent: 0,
2014-04-20 00:03:47 +09:00
}
2014-04-18 21:28:00 +09:00
return ret , nil
}
2014-04-20 01:53:34 +09:00
2016-03-22 23:09:12 +09:00
func Partitions ( all bool ) ( [ ] PartitionStat , error ) {
2017-12-31 15:25:49 +09:00
return PartitionsWithContext ( context . Background ( ) , all )
}
func PartitionsWithContext ( ctx context . Context , all bool ) ( [ ] PartitionStat , error ) {
2016-03-22 23:09:12 +09:00
var ret [ ] PartitionStat
2014-04-20 01:53:34 +09:00
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
}
2017-06-02 13:51:00 -07:00
typepath , _ := windows . UTF16PtrFromString ( path )
2014-04-20 01:53:34 +09:00
typeret , _ , _ := procGetDriveType . Call ( uintptr ( unsafe . Pointer ( typepath ) ) )
if typeret == 0 {
2017-06-02 13:51:00 -07:00
return ret , windows . GetLastError ( )
2014-04-20 01:53:34 +09:00
}
2017-05-08 09:50:38 -07:00
// 2: DRIVE_REMOVABLE 3: DRIVE_FIXED 4: DRIVE_REMOTE 5: DRIVE_CDROM
2014-04-20 01:53:34 +09:00
2017-05-08 09:50:38 -07:00
if typeret == 2 || typeret == 3 || typeret == 4 || typeret == 5 {
2014-04-20 01:53:34 +09:00
lpVolumeNameBuffer := make ( [ ] byte , 256 )
lpVolumeSerialNumber := int64 ( 0 )
lpMaximumComponentLength := int64 ( 0 )
lpFileSystemFlags := int64 ( 0 )
lpFileSystemNameBuffer := make ( [ ] byte , 256 )
2017-06-02 13:51:00 -07:00
volpath , _ := windows . UTF16PtrFromString ( string ( v ) + ":/" )
2014-04-20 01:53:34 +09:00
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 {
2017-05-08 14:23:46 -07:00
if typeret == 5 || typeret == 2 {
2015-09-04 11:51:34 -06:00
continue //device is not ready will happen if there is no disk in the drive
}
2014-04-20 01:53:34 +09:00
return ret , err
}
opts := "rw"
2014-08-15 20:05:26 +02:00
if lpFileSystemFlags & FileReadOnlyVolume != 0 {
2014-04-20 01:53:34 +09:00
opts = "ro"
}
2014-08-15 20:05:26 +02:00
if lpFileSystemFlags & FileFileCompression != 0 {
2014-04-20 01:53:34 +09:00
opts += ".compress"
}
2016-03-22 23:09:12 +09:00
d := PartitionStat {
2014-04-20 01:53:34 +09:00
Mountpoint : path ,
Device : path ,
Fstype : string ( bytes . Replace ( lpFileSystemNameBuffer , [ ] byte ( "\x00" ) , [ ] byte ( "" ) , - 1 ) ) ,
Opts : opts ,
}
ret = append ( ret , d )
}
}
}
return ret , nil
}
2014-04-29 14:59:22 +09:00
2017-04-13 18:53:09 -07:00
func IOCounters ( names ... string ) ( map [ string ] IOCountersStat , error ) {
2017-12-31 15:25:49 +09:00
return IOCountersWithContext ( context . Background ( ) , names ... )
}
func IOCountersWithContext ( ctx context . Context , names ... string ) ( map [ string ] IOCountersStat , error ) {
2016-03-22 23:09:12 +09:00
ret := make ( map [ string ] IOCountersStat , 0 )
2015-09-05 22:53:18 +09:00
var dst [ ] Win32_PerfFormattedData
2015-02-19 23:52:06 +09:00
2017-11-09 15:44:26 -08:00
err := common . WMIQueryWithContext ( ctx , "SELECT * FROM Win32_PerfFormattedData_PerfDisk_LogicalDisk" , & dst )
2015-09-05 22:53:18 +09:00
if err != nil {
2015-02-19 23:52:06 +09:00
return ret , err
}
2015-09-05 22:53:18 +09:00
for _ , d := range dst {
if len ( d . Name ) > 3 { // not get _Total or Harddrive
continue
2015-02-19 23:52:06 +09:00
}
2017-04-06 17:54:50 -07:00
2017-04-10 22:24:36 +09:00
if len ( names ) > 0 && ! common . StringsHas ( names , d . Name ) {
2017-04-06 17:54:50 -07:00
continue
}
2018-06-22 09:34:39 +08:00
tmpIO := IOCountersStat {
2015-09-05 22:53:18 +09:00
Name : d . Name ,
ReadCount : uint64 ( d . AvgDiskReadQueueLength ) ,
WriteCount : d . AvgDiskWriteQueueLength ,
ReadBytes : uint64 ( d . AvgDiskBytesPerRead ) ,
WriteBytes : uint64 ( d . AvgDiskBytesPerWrite ) ,
ReadTime : d . AvgDisksecPerRead ,
WriteTime : d . AvgDisksecPerWrite ,
2015-02-19 23:52:06 +09:00
}
2018-06-22 09:34:39 +08:00
tmpIO . SerialNumber = GetDiskSerialNumber ( d . Name )
2018-06-22 10:03:04 +08:00
ret [ d . Name ] = tmpIO
2015-02-19 23:52:06 +09:00
}
return ret , nil
2014-04-29 14:59:22 +09:00
}
2018-06-22 09:34:39 +08:00
// return disk serial number(not volume serial number) of given device or empty string on error. Name of device is drive letter, eg. C:
func GetDiskSerialNumber ( name string ) string {
return GetDiskSerialNumberWithContext ( context . Background ( ) , name )
}
func GetDiskSerialNumberWithContext ( ctx context . Context , name string ) string {
var diskPart [ ] Win32_DiskPartition
var diskDrive [ ] Win32_DiskDrive
err := common . WMIQueryWithContext ( ctx , "Associators of {Win32_LogicalDisk.DeviceID='" + name + "'} where AssocClass=Win32_LogicalDiskToPartition" , & diskPart )
if err != nil || len ( diskPart ) <= 0 {
return ""
}
err = common . WMIQueryWithContext ( ctx , "Associators of {Win32_DiskPartition.DeviceID='" + diskPart [ 0 ] . DeviceID + "'} where AssocClass=Win32_DiskDriveToDiskPartition" , & diskDrive )
if err != nil || len ( diskDrive ) <= 0 {
return ""
}
return diskDrive [ 0 ] . SerialNumber
}