1
2
3
4
5 package oshi.hardware.platform.windows;
6
7 import java.util.ArrayList;
8 import java.util.Collections;
9 import java.util.Comparator;
10 import java.util.HashMap;
11 import java.util.List;
12 import java.util.Locale;
13 import java.util.Map;
14 import java.util.Objects;
15 import java.util.regex.Matcher;
16 import java.util.regex.Pattern;
17 import java.util.stream.Collectors;
18
19 import org.slf4j.Logger;
20 import org.slf4j.LoggerFactory;
21
22 import com.sun.jna.platform.win32.Kernel32;
23 import com.sun.jna.platform.win32.COM.COMException;
24 import com.sun.jna.platform.win32.COM.WbemcliUtil.WmiResult;
25
26 import oshi.annotation.concurrent.ThreadSafe;
27 import oshi.driver.windows.perfmon.PhysicalDisk;
28 import oshi.driver.windows.perfmon.PhysicalDisk.PhysicalDiskProperty;
29 import oshi.driver.windows.wmi.Win32DiskDrive;
30 import oshi.driver.windows.wmi.Win32DiskDrive.DiskDriveProperty;
31 import oshi.driver.windows.wmi.Win32DiskDriveToDiskPartition;
32 import oshi.driver.windows.wmi.Win32DiskDriveToDiskPartition.DriveToPartitionProperty;
33 import oshi.driver.windows.wmi.Win32DiskPartition;
34 import oshi.driver.windows.wmi.Win32DiskPartition.DiskPartitionProperty;
35 import oshi.driver.windows.wmi.Win32LogicalDiskToPartition;
36 import oshi.driver.windows.wmi.Win32LogicalDiskToPartition.DiskToPartitionProperty;
37 import oshi.hardware.HWDiskStore;
38 import oshi.hardware.HWPartition;
39 import oshi.hardware.common.AbstractHWDiskStore;
40 import oshi.util.ParseUtil;
41 import oshi.util.platform.windows.WmiQueryHandler;
42 import oshi.util.platform.windows.WmiUtil;
43 import oshi.util.tuples.Pair;
44
45
46
47
48 @ThreadSafe
49 public final class WindowsHWDiskStore extends AbstractHWDiskStore {
50
51 private static final Logger LOG = LoggerFactory.getLogger(WindowsHWDiskStore.class);
52
53 private static final String PHYSICALDRIVE_PREFIX = "\\\\.\\PHYSICALDRIVE";
54 private static final Pattern DEVICE_ID = Pattern.compile(".*\\.DeviceID=\"(.*)\"");
55
56
57
58 private static final int GUID_BUFSIZE = 100;
59
60 private long reads = 0L;
61 private long readBytes = 0L;
62 private long writes = 0L;
63 private long writeBytes = 0L;
64 private long currentQueueLength = 0L;
65 private long transferTime = 0L;
66 private long timeStamp = 0L;
67 private List<HWPartition> partitionList;
68
69 private WindowsHWDiskStore(String name, String model, String serial, long size) {
70 super(name, model, serial, size);
71 }
72
73 @Override
74 public long getReads() {
75 return reads;
76 }
77
78 @Override
79 public long getReadBytes() {
80 return readBytes;
81 }
82
83 @Override
84 public long getWrites() {
85 return writes;
86 }
87
88 @Override
89 public long getWriteBytes() {
90 return writeBytes;
91 }
92
93 @Override
94 public long getCurrentQueueLength() {
95 return currentQueueLength;
96 }
97
98 @Override
99 public long getTransferTime() {
100 return transferTime;
101 }
102
103 @Override
104 public long getTimeStamp() {
105 return timeStamp;
106 }
107
108 @Override
109 public List<HWPartition> getPartitions() {
110 return this.partitionList;
111 }
112
113 @Override
114 public boolean updateAttributes() {
115 String index = null;
116 List<HWPartition> partitions = getPartitions();
117 if (!partitions.isEmpty()) {
118
119
120 index = Integer.toString(partitions.get(0).getMajor());
121 } else if (getName().startsWith(PHYSICALDRIVE_PREFIX)) {
122
123
124
125
126
127 index = getName().substring(PHYSICALDRIVE_PREFIX.length(), getName().length());
128 } else {
129
130
131
132
133 LOG.warn("Couldn't match index for {}", getName());
134 return false;
135 }
136 DiskStats stats = queryReadWriteStats(index);
137 if (stats.readMap.containsKey(index)) {
138 this.reads = stats.readMap.getOrDefault(index, 0L);
139 this.readBytes = stats.readByteMap.getOrDefault(index, 0L);
140 this.writes = stats.writeMap.getOrDefault(index, 0L);
141 this.writeBytes = stats.writeByteMap.getOrDefault(index, 0L);
142 this.currentQueueLength = stats.queueLengthMap.getOrDefault(index, 0L);
143 this.transferTime = stats.diskTimeMap.getOrDefault(index, 0L);
144 this.timeStamp = stats.timeStamp;
145 return true;
146 } else {
147 return false;
148 }
149 }
150
151
152
153
154
155
156 public static List<HWDiskStore> getDisks() {
157 WmiQueryHandler h = Objects.requireNonNull(WmiQueryHandler.createInstance());
158 boolean comInit = false;
159 try {
160 comInit = h.initCOM();
161 List<HWDiskStore> result;
162 result = new ArrayList<>();
163 DiskStats stats = queryReadWriteStats(null);
164 PartitionMaps maps = queryPartitionMaps(h);
165
166 WmiResult<DiskDriveProperty> vals = Win32DiskDrive.queryDiskDrive(h);
167 for (int i = 0; i < vals.getResultCount(); i++) {
168 WindowsHWDiskStore ds = new WindowsHWDiskStore(WmiUtil.getString(vals, DiskDriveProperty.NAME, i),
169 String.format(Locale.ROOT, "%s %s", WmiUtil.getString(vals, DiskDriveProperty.MODEL, i),
170 WmiUtil.getString(vals, DiskDriveProperty.MANUFACTURER, i)).trim(),
171
172 ParseUtil.hexStringToString(WmiUtil.getString(vals, DiskDriveProperty.SERIALNUMBER, i)),
173 WmiUtil.getUint64(vals, DiskDriveProperty.SIZE, i));
174
175 String index = Integer.toString(WmiUtil.getUint32(vals, DiskDriveProperty.INDEX, i));
176 ds.reads = stats.readMap.getOrDefault(index, 0L);
177 ds.readBytes = stats.readByteMap.getOrDefault(index, 0L);
178 ds.writes = stats.writeMap.getOrDefault(index, 0L);
179 ds.writeBytes = stats.writeByteMap.getOrDefault(index, 0L);
180 ds.currentQueueLength = stats.queueLengthMap.getOrDefault(index, 0L);
181
182
183
184
185
186
187 ds.transferTime = stats.diskTimeMap.getOrDefault(index, 0L);
188 ds.timeStamp = stats.timeStamp;
189
190 List<HWPartition> partitions = new ArrayList<>();
191 List<String> partList = maps.driveToPartitionMap.get(ds.getName());
192 if (partList != null && !partList.isEmpty()) {
193 for (String part : partList) {
194 if (maps.partitionMap.containsKey(part)) {
195 partitions.addAll(maps.partitionMap.get(part));
196 }
197 }
198 }
199 ds.partitionList = Collections.unmodifiableList(partitions.stream()
200 .sorted(Comparator.comparing(HWPartition::getName)).collect(Collectors.toList()));
201
202 result.add(ds);
203 }
204 return result;
205 } catch (COMException e) {
206 LOG.warn("COM exception: {}", e.getMessage());
207 return Collections.emptyList();
208 } finally {
209 if (comInit) {
210 h.unInitCOM();
211 }
212 }
213 }
214
215
216
217
218
219
220
221 private static DiskStats queryReadWriteStats(String index) {
222
223 DiskStats stats = new DiskStats();
224 Pair<List<String>, Map<PhysicalDiskProperty, List<Long>>> instanceValuePair = PhysicalDisk.queryDiskCounters();
225 List<String> instances = instanceValuePair.getA();
226 Map<PhysicalDiskProperty, List<Long>> valueMap = instanceValuePair.getB();
227 stats.timeStamp = System.currentTimeMillis();
228 List<Long> readList = valueMap.get(PhysicalDiskProperty.DISKREADSPERSEC);
229 List<Long> readByteList = valueMap.get(PhysicalDiskProperty.DISKREADBYTESPERSEC);
230 List<Long> writeList = valueMap.get(PhysicalDiskProperty.DISKWRITESPERSEC);
231 List<Long> writeByteList = valueMap.get(PhysicalDiskProperty.DISKWRITEBYTESPERSEC);
232 List<Long> queueLengthList = valueMap.get(PhysicalDiskProperty.CURRENTDISKQUEUELENGTH);
233 List<Long> diskTimeList = valueMap.get(PhysicalDiskProperty.PERCENTDISKTIME);
234
235 if (instances.isEmpty() || readList == null || readByteList == null || writeList == null
236 || writeByteList == null || queueLengthList == null || diskTimeList == null) {
237 return stats;
238 }
239 for (int i = 0; i < instances.size(); i++) {
240 String name = getIndexFromName(instances.get(i));
241
242 if (index != null && !index.equals(name)) {
243 continue;
244 }
245 stats.readMap.put(name, readList.get(i));
246 stats.readByteMap.put(name, readByteList.get(i));
247 stats.writeMap.put(name, writeList.get(i));
248 stats.writeByteMap.put(name, writeByteList.get(i));
249 stats.queueLengthMap.put(name, queueLengthList.get(i));
250 stats.diskTimeMap.put(name, diskTimeList.get(i) / 10_000L);
251 }
252 return stats;
253 }
254
255 private static PartitionMaps queryPartitionMaps(WmiQueryHandler h) {
256
257 PartitionMaps maps = new PartitionMaps();
258
259
260 Matcher mAnt;
261 Matcher mDep;
262
263
264 WmiResult<DriveToPartitionProperty> drivePartitionMap = Win32DiskDriveToDiskPartition.queryDriveToPartition(h);
265 for (int i = 0; i < drivePartitionMap.getResultCount(); i++) {
266 mAnt = DEVICE_ID.matcher(WmiUtil.getRefString(drivePartitionMap, DriveToPartitionProperty.ANTECEDENT, i));
267 mDep = DEVICE_ID.matcher(WmiUtil.getRefString(drivePartitionMap, DriveToPartitionProperty.DEPENDENT, i));
268 if (mAnt.matches() && mDep.matches()) {
269 maps.driveToPartitionMap.computeIfAbsent(mAnt.group(1).replace("\\\\", "\\"), x -> new ArrayList<>())
270 .add(mDep.group(1));
271 }
272 }
273
274
275 WmiResult<DiskToPartitionProperty> diskPartitionMap = Win32LogicalDiskToPartition.queryDiskToPartition(h);
276 for (int i = 0; i < diskPartitionMap.getResultCount(); i++) {
277 mAnt = DEVICE_ID.matcher(WmiUtil.getRefString(diskPartitionMap, DiskToPartitionProperty.ANTECEDENT, i));
278 mDep = DEVICE_ID.matcher(WmiUtil.getRefString(diskPartitionMap, DiskToPartitionProperty.DEPENDENT, i));
279 long size = WmiUtil.getUint64(diskPartitionMap, DiskToPartitionProperty.ENDINGADDRESS, i)
280 - WmiUtil.getUint64(diskPartitionMap, DiskToPartitionProperty.STARTINGADDRESS, i) + 1L;
281 if (mAnt.matches() && mDep.matches()) {
282 if (maps.partitionToLogicalDriveMap.containsKey(mAnt.group(1))) {
283 maps.partitionToLogicalDriveMap.get(mAnt.group(1)).add(new Pair<>(mDep.group(1) + "\\", size));
284 } else {
285 List<Pair<String, Long>> list = new ArrayList<>();
286 list.add(new Pair<>(mDep.group(1) + "\\", size));
287 maps.partitionToLogicalDriveMap.put(mAnt.group(1), list);
288 }
289 }
290 }
291
292
293 WmiResult<DiskPartitionProperty> hwPartitionQueryMap = Win32DiskPartition.queryPartition(h);
294 for (int i = 0; i < hwPartitionQueryMap.getResultCount(); i++) {
295 String deviceID = WmiUtil.getString(hwPartitionQueryMap, DiskPartitionProperty.DEVICEID, i);
296 List<Pair<String, Long>> logicalDrives = maps.partitionToLogicalDriveMap.get(deviceID);
297 if (logicalDrives == null) {
298 continue;
299 }
300 for (int j = 0; j < logicalDrives.size(); j++) {
301 Pair<String, Long> logicalDrive = logicalDrives.get(j);
302 if (logicalDrive != null && !logicalDrive.getA().isEmpty()) {
303 char[] volumeChr = new char[GUID_BUFSIZE];
304 Kernel32.INSTANCE.GetVolumeNameForVolumeMountPoint(logicalDrive.getA(), volumeChr, GUID_BUFSIZE);
305 String uuid = ParseUtil.parseUuidOrDefault(new String(volumeChr).trim(), "");
306 HWPartition pt = new HWPartition(
307 WmiUtil.getString(hwPartitionQueryMap, DiskPartitionProperty.NAME, i),
308 WmiUtil.getString(hwPartitionQueryMap, DiskPartitionProperty.TYPE, i),
309 WmiUtil.getString(hwPartitionQueryMap, DiskPartitionProperty.DESCRIPTION, i), uuid,
310 logicalDrive.getB(),
311 WmiUtil.getUint32(hwPartitionQueryMap, DiskPartitionProperty.DISKINDEX, i),
312 WmiUtil.getUint32(hwPartitionQueryMap, DiskPartitionProperty.INDEX, i),
313 logicalDrive.getA());
314 if (maps.partitionMap.containsKey(deviceID)) {
315 maps.partitionMap.get(deviceID).add(pt);
316 } else {
317 List<HWPartition> ptlist = new ArrayList<>();
318 ptlist.add(pt);
319 maps.partitionMap.put(deviceID, ptlist);
320 }
321 }
322 }
323 }
324 return maps;
325 }
326
327
328
329
330
331
332
333 private static String getIndexFromName(String s) {
334 if (s.isEmpty()) {
335 return s;
336 }
337 return s.split("\\s")[0];
338 }
339
340
341
342
343 private static final class DiskStats {
344 private final Map<String, Long> readMap = new HashMap<>();
345 private final Map<String, Long> readByteMap = new HashMap<>();
346 private final Map<String, Long> writeMap = new HashMap<>();
347 private final Map<String, Long> writeByteMap = new HashMap<>();
348 private final Map<String, Long> queueLengthMap = new HashMap<>();
349 private final Map<String, Long> diskTimeMap = new HashMap<>();
350 private long timeStamp;
351 }
352
353
354
355
356 private static final class PartitionMaps {
357 private final Map<String, List<String>> driveToPartitionMap = new HashMap<>();
358 private final Map<String, List<Pair<String, Long>>> partitionToLogicalDriveMap = new HashMap<>();
359 private final Map<String, List<HWPartition>> partitionMap = new HashMap<>();
360 }
361 }