...

Source file src/github.com/shirou/gopsutil/disk/disk_windows.go

Documentation: github.com/shirou/gopsutil/disk

     1  // +build windows
     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)     // 0x00000010
    25  	FileReadOnlyVolume  = int64(524288) // 0x00080000
    26  )
    27  
    28  // diskPerformance is an equivalent representation of DISK_PERFORMANCE in the Windows API.
    29  // https://docs.microsoft.com/fr-fr/windows/win32/api/winioctl/ns-winioctl-disk_performance
    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 // necessary for 32bit support, see https://github.com/elastic/beats/pull/16553
    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  		// InodesTotal: 0,
    65  		// InodesFree: 0,
    66  		// InodesUsed: 0,
    67  		// InodesUsedPercent: 0,
    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  			// 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  
   134  func IOCountersWithContext(ctx context.Context, names ...string) (map[string]IOCountersStat, error) {
   135  	// https://github.com/giampaolo/psutil/blob/544e9daa4f66a9f80d7bf6c7886d693ee42f0a13/psutil/arch/windows/disk.c#L83
   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), // convert to ms: https://github.com/giampaolo/psutil/issues/1012
   177  				WriteTime:  uint64(diskPerformance.WriteTime / 10000 / 1000),
   178  				Name:       path,
   179  			}
   180  		}
   181  	}
   182  	return drivemap, nil
   183  }
   184  

View as plain text