github.com/isyscore/isc-gobase@v1.5.3-0.20231218061332-cbc7451899e9/system/disk/disk_windows.go (about) 1 //go:build windows 2 3 package disk 4 5 import ( 6 "bytes" 7 "context" 8 "fmt" 9 "github.com/isyscore/isc-gobase/system/common" 10 "golang.org/x/sys/windows" 11 "syscall" 12 "unsafe" 13 ) 14 15 var ( 16 procGetDiskFreeSpaceExW = common.Modkernel32.NewProc("GetDiskFreeSpaceExW") 17 procGetLogicalDriveStringsW = common.Modkernel32.NewProc("GetLogicalDriveStringsW") 18 procGetDriveType = common.Modkernel32.NewProc("GetDriveTypeW") 19 procGetVolumeInformation = common.Modkernel32.NewProc("GetVolumeInformationW") 20 ) 21 22 var ( 23 FileFileCompression = int64(16) // 0x00000010 24 FileReadOnlyVolume = int64(524288) // 0x00080000 25 ) 26 27 // diskPerformance is an equivalent representation of DISK_PERFORMANCE in the Windows API. 28 // https://docs.microsoft.com/fr-fr/windows/win32/api/winioctl/ns-winioctl-disk_performance 29 type diskPerformance struct { 30 BytesRead int64 31 BytesWritten int64 32 ReadTime int64 33 WriteTime int64 34 IdleTime int64 35 ReadCount uint32 36 WriteCount uint32 37 QueueDepth uint32 38 SplitCount uint32 39 QueryTime int64 40 StorageDeviceNumber uint32 41 StorageManagerName [8]uint16 42 alignmentPadding uint32 // necessary for 32bit support, see https://github.com/elastic/beats/pull/16553 43 } 44 45 func UsageWithContext(ctx context.Context, path string) (*UsageStat, error) { 46 lpFreeBytesAvailable := int64(0) 47 lpTotalNumberOfBytes := int64(0) 48 lpTotalNumberOfFreeBytes := int64(0) 49 diskret, _, err := procGetDiskFreeSpaceExW.Call( 50 uintptr(unsafe.Pointer(windows.StringToUTF16Ptr(path))), 51 uintptr(unsafe.Pointer(&lpFreeBytesAvailable)), 52 uintptr(unsafe.Pointer(&lpTotalNumberOfBytes)), 53 uintptr(unsafe.Pointer(&lpTotalNumberOfFreeBytes))) 54 if diskret == 0 { 55 return nil, err 56 } 57 ret := &UsageStat{ 58 Path: path, 59 Total: uint64(lpTotalNumberOfBytes), 60 Free: uint64(lpTotalNumberOfFreeBytes), 61 Used: uint64(lpTotalNumberOfBytes) - uint64(lpTotalNumberOfFreeBytes), 62 UsedPercent: (float64(lpTotalNumberOfBytes) - float64(lpTotalNumberOfFreeBytes)) / float64(lpTotalNumberOfBytes) * 100, 63 } 64 return ret, nil 65 } 66 67 func PartitionsWithContext(ctx context.Context, all bool) ([]PartitionStat, error) { 68 var ret []PartitionStat 69 lpBuffer := make([]byte, 254) 70 diskret, _, err := procGetLogicalDriveStringsW.Call( 71 uintptr(len(lpBuffer)), 72 uintptr(unsafe.Pointer(&lpBuffer[0]))) 73 if diskret == 0 { 74 return ret, err 75 } 76 for _, v := range lpBuffer { 77 if v >= 65 && v <= 90 { 78 path := string(v) + ":" 79 typepath, _ := windows.UTF16PtrFromString(path) 80 typeret, _, _ := procGetDriveType.Call(uintptr(unsafe.Pointer(typepath))) 81 if typeret == 0 { 82 return ret, windows.GetLastError() 83 } 84 // 2: DRIVE_REMOVABLE 3: DRIVE_FIXED 4: DRIVE_REMOTE 5: DRIVE_CDROM 85 86 if typeret == 2 || typeret == 3 || typeret == 4 || typeret == 5 { 87 lpVolumeNameBuffer := make([]byte, 256) 88 lpVolumeSerialNumber := int64(0) 89 lpMaximumComponentLength := int64(0) 90 lpFileSystemFlags := int64(0) 91 lpFileSystemNameBuffer := make([]byte, 256) 92 volpath, _ := windows.UTF16PtrFromString(string(v) + ":/") 93 driveret, _, err := procGetVolumeInformation.Call( 94 uintptr(unsafe.Pointer(volpath)), 95 uintptr(unsafe.Pointer(&lpVolumeNameBuffer[0])), 96 uintptr(len(lpVolumeNameBuffer)), 97 uintptr(unsafe.Pointer(&lpVolumeSerialNumber)), 98 uintptr(unsafe.Pointer(&lpMaximumComponentLength)), 99 uintptr(unsafe.Pointer(&lpFileSystemFlags)), 100 uintptr(unsafe.Pointer(&lpFileSystemNameBuffer[0])), 101 uintptr(len(lpFileSystemNameBuffer))) 102 if driveret == 0 { 103 if typeret == 5 || typeret == 2 { 104 continue //device is not ready will happen if there is no disk in the drive 105 } 106 return ret, err 107 } 108 opts := "rw" 109 if lpFileSystemFlags&FileReadOnlyVolume != 0 { 110 opts = "ro" 111 } 112 if lpFileSystemFlags&FileFileCompression != 0 { 113 opts += ".compress" 114 } 115 116 d := PartitionStat{ 117 Mountpoint: path, 118 Device: path, 119 Fstype: string(bytes.Replace(lpFileSystemNameBuffer, []byte("\x00"), []byte(""), -1)), 120 Opts: opts, 121 } 122 ret = append(ret, d) 123 } 124 } 125 } 126 return ret, nil 127 } 128 129 func IOCountersWithContext(ctx context.Context, names ...string) (map[string]IOCountersStat, error) { 130 // https://github.com/giampaolo/psutil/blob/544e9daa4f66a9f80d7bf6c7886d693ee42f0a13/psutil/arch/windows/disk.c#L83 131 drivemap := make(map[string]IOCountersStat, 0) 132 var diskPerformance diskPerformance 133 134 lpBuffer := make([]uint16, 254) 135 lpBufferLen, err := windows.GetLogicalDriveStrings(uint32(len(lpBuffer)), &lpBuffer[0]) 136 if err != nil { 137 return drivemap, err 138 } 139 for _, v := range lpBuffer[:lpBufferLen] { 140 if 'A' <= v && v <= 'Z' { 141 path := string(rune(v)) + ":" 142 typepath, _ := windows.UTF16PtrFromString(path) 143 typeret := windows.GetDriveType(typepath) 144 if typeret == 0 { 145 return drivemap, windows.GetLastError() 146 } 147 if typeret != windows.DRIVE_FIXED { 148 continue 149 } 150 szDevice := fmt.Sprintf(`\\.\%s`, path) 151 const IOCTL_DISK_PERFORMANCE = 0x70020 152 h, err := windows.CreateFile(syscall.StringToUTF16Ptr(szDevice), 0, windows.FILE_SHARE_READ|windows.FILE_SHARE_WRITE, nil, windows.OPEN_EXISTING, 0, 0) 153 if err != nil { 154 if err == windows.ERROR_FILE_NOT_FOUND { 155 continue 156 } 157 return drivemap, err 158 } 159 defer func(handle windows.Handle) { 160 _ = windows.CloseHandle(handle) 161 }(h) 162 163 var diskPerformanceSize uint32 164 err = windows.DeviceIoControl(h, IOCTL_DISK_PERFORMANCE, nil, 0, (*byte)(unsafe.Pointer(&diskPerformance)), uint32(unsafe.Sizeof(diskPerformance)), &diskPerformanceSize, nil) 165 if err != nil { 166 return drivemap, err 167 } 168 drivemap[path] = IOCountersStat{ 169 ReadBytes: uint64(diskPerformance.BytesRead), 170 WriteBytes: uint64(diskPerformance.BytesWritten), 171 ReadCount: uint64(diskPerformance.ReadCount), 172 WriteCount: uint64(diskPerformance.WriteCount), 173 ReadTime: uint64(diskPerformance.ReadTime / 10000 / 1000), // convert to ms: https://github.com/giampaolo/psutil/issues/1012 174 WriteTime: uint64(diskPerformance.WriteTime / 10000 / 1000), 175 Name: path, 176 } 177 } 178 } 179 return drivemap, nil 180 }