View Javadoc
1   /*
2    * Copyright 2021-2022 The OSHI Project Contributors
3    * SPDX-License-Identifier: MIT
4    */
5   package oshi.hardware.platform.windows;
6   
7   import java.util.ArrayList;
8   import java.util.Collections;
9   import java.util.HashMap;
10  import java.util.HashSet;
11  import java.util.List;
12  import java.util.Map;
13  import java.util.Map.Entry;
14  import java.util.Objects;
15  import java.util.Set;
16  import java.util.regex.Matcher;
17  import java.util.regex.Pattern;
18  
19  import org.slf4j.Logger;
20  import org.slf4j.LoggerFactory;
21  
22  import com.sun.jna.platform.win32.VersionHelpers;
23  import com.sun.jna.platform.win32.COM.COMException;
24  import com.sun.jna.platform.win32.COM.WbemcliUtil.WmiResult;
25  
26  import oshi.driver.windows.wmi.MSFTStorage;
27  import oshi.driver.windows.wmi.MSFTStorage.PhysicalDiskProperty;
28  import oshi.driver.windows.wmi.MSFTStorage.StoragePoolProperty;
29  import oshi.driver.windows.wmi.MSFTStorage.StoragePoolToPhysicalDiskProperty;
30  import oshi.driver.windows.wmi.MSFTStorage.VirtualDiskProperty;
31  import oshi.hardware.LogicalVolumeGroup;
32  import oshi.hardware.common.AbstractLogicalVolumeGroup;
33  import oshi.util.ParseUtil;
34  import oshi.util.platform.windows.WmiQueryHandler;
35  import oshi.util.platform.windows.WmiUtil;
36  import oshi.util.tuples.Pair;
37  
38  final class WindowsLogicalVolumeGroup extends AbstractLogicalVolumeGroup {
39  
40      private static final Logger LOG = LoggerFactory.getLogger(WindowsLogicalVolumeGroup.class);
41  
42      private static final Pattern SP_OBJECT_ID = Pattern.compile(".*ObjectId=.*SP:(\\{.*\\}).*");
43      private static final Pattern PD_OBJECT_ID = Pattern.compile(".*ObjectId=.*PD:(\\{.*\\}).*");
44      private static final Pattern VD_OBJECT_ID = Pattern.compile(".*ObjectId=.*VD:(\\{.*\\})(\\{.*\\}).*");
45  
46      private static final boolean IS_WINDOWS8_OR_GREATER = VersionHelpers.IsWindows8OrGreater();
47  
48      WindowsLogicalVolumeGroup(String name, Map<String, Set<String>> lvMap, Set<String> pvSet) {
49          super(name, lvMap, pvSet);
50      }
51  
52      static List<LogicalVolumeGroup> getLogicalVolumeGroups() {
53          // Storage Spaces requires Windows 8 or Server 2012
54          if (!IS_WINDOWS8_OR_GREATER) {
55              return Collections.emptyList();
56          }
57          WmiQueryHandler h = Objects.requireNonNull(WmiQueryHandler.createInstance());
58          boolean comInit = false;
59          try {
60              comInit = h.initCOM();
61              // Query Storage Pools first, so we can skip other queries if we have no pools
62              WmiResult<StoragePoolProperty> sp = MSFTStorage.queryStoragePools(h);
63              int count = sp.getResultCount();
64              if (count == 0) {
65                  return Collections.emptyList();
66              }
67              // We have storage pool(s) but now need to gather other info
68  
69              // Get all the Virtual Disks
70              Map<String, String> vdMap = new HashMap<>();
71              WmiResult<VirtualDiskProperty> vds = MSFTStorage.queryVirtualDisks(h);
72              count = vds.getResultCount();
73              for (int i = 0; i < count; i++) {
74                  String vdObjectId = WmiUtil.getString(vds, VirtualDiskProperty.OBJECTID, i);
75                  Matcher m = VD_OBJECT_ID.matcher(vdObjectId);
76                  if (m.matches()) {
77                      vdObjectId = m.group(2) + " " + m.group(1);
78                  }
79                  // Store key with SP|VD
80                  vdMap.put(vdObjectId, WmiUtil.getString(vds, VirtualDiskProperty.FRIENDLYNAME, i));
81              }
82  
83              // Get all the Physical Disks
84              Map<String, Pair<String, String>> pdMap = new HashMap<>();
85              WmiResult<PhysicalDiskProperty> pds = MSFTStorage.queryPhysicalDisks(h);
86              count = pds.getResultCount();
87              for (int i = 0; i < count; i++) {
88                  String pdObjectId = WmiUtil.getString(pds, PhysicalDiskProperty.OBJECTID, i);
89                  Matcher m = PD_OBJECT_ID.matcher(pdObjectId);
90                  if (m.matches()) {
91                      pdObjectId = m.group(1);
92                  }
93                  // Store key with PD
94                  pdMap.put(pdObjectId, new Pair<>(WmiUtil.getString(pds, PhysicalDiskProperty.FRIENDLYNAME, i),
95                          WmiUtil.getString(pds, PhysicalDiskProperty.PHYSICALLOCATION, i)));
96              }
97  
98              // Get the Storage Pool to Physical Disk mappping
99              Map<String, String> sppdMap = new HashMap<>();
100             WmiResult<StoragePoolToPhysicalDiskProperty> sppd = MSFTStorage.queryStoragePoolPhysicalDisks(h);
101             count = sppd.getResultCount();
102             for (int i = 0; i < count; i++) {
103                 // Ref string contains object id, will do partial match later
104                 String spObjectId = WmiUtil.getRefString(sppd, StoragePoolToPhysicalDiskProperty.STORAGEPOOL, i);
105                 Matcher m = SP_OBJECT_ID.matcher(spObjectId);
106                 if (m.matches()) {
107                     spObjectId = m.group(1);
108                 }
109                 String pdObjectId = WmiUtil.getRefString(sppd, StoragePoolToPhysicalDiskProperty.PHYSICALDISK, i);
110                 m = PD_OBJECT_ID.matcher(pdObjectId);
111                 if (m.matches()) {
112                     pdObjectId = m.group(1);
113                 }
114                 sppdMap.put(spObjectId + " " + pdObjectId, pdObjectId);
115             }
116 
117             // Finally process the storage pools
118             List<LogicalVolumeGroup> lvgList = new ArrayList<>();
119             count = sp.getResultCount();
120             for (int i = 0; i < count; i++) {
121                 // Name
122                 String name = WmiUtil.getString(sp, StoragePoolProperty.FRIENDLYNAME, i);
123                 // Parse object ID to match
124                 String spObjectId = WmiUtil.getString(sp, StoragePoolProperty.OBJECTID, i);
125                 Matcher m = SP_OBJECT_ID.matcher(spObjectId);
126                 if (m.matches()) {
127                     spObjectId = m.group(1);
128                 }
129                 // find matching physical and logical volumes
130                 Set<String> physicalVolumeSet = new HashSet<>();
131                 for (Entry<String, String> entry : sppdMap.entrySet()) {
132                     if (entry.getKey().contains(spObjectId)) {
133                         String pdObjectId = entry.getValue();
134                         Pair<String, String> nameLoc = pdMap.get(pdObjectId);
135                         if (nameLoc != null) {
136                             physicalVolumeSet.add(nameLoc.getA() + " @ " + nameLoc.getB());
137                         }
138                     }
139                 }
140                 // find matching logical volume
141                 Map<String, Set<String>> logicalVolumeMap = new HashMap<>();
142                 for (Entry<String, String> entry : vdMap.entrySet()) {
143                     if (entry.getKey().contains(spObjectId)) {
144                         String vdObjectId = ParseUtil.whitespaces.split(entry.getKey())[0];
145                         logicalVolumeMap.put(entry.getValue() + " " + vdObjectId, physicalVolumeSet);
146                     }
147                 }
148                 // Add to list
149                 lvgList.add(new WindowsLogicalVolumeGroup(name, logicalVolumeMap, physicalVolumeSet));
150             }
151 
152             return lvgList;
153         } catch (COMException e) {
154             LOG.warn("COM exception: {}", e.getMessage());
155             return Collections.emptyList();
156         } finally {
157             if (comInit) {
158                 h.unInitCOM();
159             }
160         }
161     }
162 }