Skip to content

feat: Use WMI to implement Disk API to reduce PowerShell overhead (library version) #385

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion .github/workflows/windows.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,11 @@ jobs:
Install-WindowsFeature -name Hyper-V-PowerShell

$env:CSI_PROXY_GH_ACTIONS="TRUE"
go test --timeout 20m -v ./integrationtests/...
# Note: checkptr is enabled by default with `-race` specified.
# Since Windows COM interop is called in integration tests and
# dereferencing pointer allocated by COM is not safe for Go,
# checkptr needs to be disabled for these tests to pass.
go test --timeout 20m -v -race -gcflags=all=-d=checkptr=0 ./integrationtests/...
unit_tests:
strategy:
matrix:
Expand Down
11 changes: 11 additions & 0 deletions docs/IMPLEMENTATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,17 @@ can be used to retrieve a volume (`MSFT_Volume`) from a partition (`MSFT_Partiti
collection, err := part.GetAssociated("MSFT_PartitionToVolume", "MSFT_Volume", "Volume", "Partition")
```

### Disable checkptr

COM APIs (like IDispatch.Invoke, Variant, BSTR, IUnknown**, etc.) frequently use:
* Double pointers (e.g., BSTR*, IUnknown**)
* Pointers passed as int64 or uintptr
* In-place modification via reference

These patterns are common and valid in C/C++, but violate Go’s unsafe.Pointer rules when interpreted literally - even if they are functionally correct.

Therefore, you should make sure `-gcflags=all=-d=checkptr=0` when calling `microsoft/wmi`.

<a name="debug-powershell"></a>
## Debug with PowerShell

Expand Down
127 changes: 127 additions & 0 deletions pkg/cim/disk.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,14 @@ const (
ErrorCodeCreatePartitionAccessPathAlreadyInUse = 42002
)

var (
DiskSelectorListForDiskNumberAndLocation = []string{"Number", "Location"}
DiskSelectorListForPartitionStyle = []string{"PartitionStyle"}
DiskSelectorListForPathAndSerialNumber = []string{"Path", "SerialNumber"}
DiskSelectorListForIsOffline = []string{"IsOffline"}
DiskSelectorListForSize = []string{"Size"}
)

// QueryDiskByNumber retrieves disk information for a specific disk identified by its number.
//
// The equivalent WMI query is:
Expand All @@ -52,6 +60,43 @@ func QueryDiskByNumber(diskNumber uint32, selectorList []string) (*storage.MSFT_
return disk, nil
}

// ListDisks retrieves information about all available disks.
//
// The equivalent WMI query is:
//
// SELECT [selectors] FROM MSFT_Disk
//
// Refer to https://learn.microsoft.com/en-us/windows-hardware/drivers/storage/msft-disk
// for the WMI class definition.
func ListDisks(selectorList []string) ([]*storage.MSFT_Disk, error) {
diskQuery := query.NewWmiQueryWithSelectList("MSFT_Disk", selectorList)
instances, err := QueryInstances(WMINamespaceStorage, diskQuery)
if IgnoreNotFound(err) != nil {
return nil, err
}

var disks []*storage.MSFT_Disk
for _, instance := range instances {
disk, err := storage.NewMSFT_DiskEx1(instance)
if err != nil {
return nil, fmt.Errorf("failed to query disk %v. error: %v", instance, err)
}

disks = append(disks, disk)
}

return disks, nil
}

// InitializeDisk initializes a RAW disk with a particular partition style.
//
// Refer to https://learn.microsoft.com/en-us/windows-hardware/drivers/storage/initialize-msft-disk
// for the WMI method definition.
func InitializeDisk(disk *storage.MSFT_Disk, partitionStyle int) (int, error) {
result, err := disk.InvokeMethodWithReturn("Initialize", int32(partitionStyle))
return int(result), err
}

// RefreshDisk Refreshes the cached disk layout information.
//
// Refer to https://learn.microsoft.com/en-us/windows-hardware/drivers/storage/msft-disk-refresh
Expand All @@ -61,3 +106,85 @@ func RefreshDisk(disk *storage.MSFT_Disk) (int, string, error) {
result, err := disk.InvokeMethodWithReturn("Refresh", &status)
return int(result), status, err
}

// CreatePartition creates a partition on a disk.
//
// Refer to https://learn.microsoft.com/en-us/windows-hardware/drivers/storage/createpartition-msft-disk
// for the WMI method definition.
func CreatePartition(disk *storage.MSFT_Disk, params ...interface{}) (int, error) {
result, err := disk.InvokeMethodWithReturn("CreatePartition", params...)
return int(result), err
}

// SetDiskState takes a disk online or offline.
//
// Refer to https://learn.microsoft.com/en-us/windows-hardware/drivers/storage/msft-disk-online and
// https://learn.microsoft.com/en-us/windows-hardware/drivers/storage/msft-disk-offline
// for the WMI method definition.
func SetDiskState(disk *storage.MSFT_Disk, online bool) (int, string, error) {
method := "Offline"
if online {
method = "Online"
}

var status string
result, err := disk.InvokeMethodWithReturn(method, &status)
return int(result), status, err
}

// RescanDisks rescans all changes by updating the internal cache of software objects (that is, Disks, Partitions, Volumes)
// for the storage setting.
//
// Refer to https://learn.microsoft.com/en-us/windows-hardware/drivers/storage/msft-storagesetting-updatehoststoragecache
// for the WMI method definition.
func RescanDisks() (int, error) {
result, _, err := InvokeCimMethod(WMINamespaceStorage, "MSFT_StorageSetting", "UpdateHostStorageCache", nil)
return result, err
}

// GetDiskNumber returns the number of a disk.
func GetDiskNumber(disk *storage.MSFT_Disk) (uint32, error) {
number, err := disk.GetProperty("Number")
if err != nil {
return 0, err
}
return uint32(number.(int32)), err
}

// GetDiskLocation returns the location of a disk.
func GetDiskLocation(disk *storage.MSFT_Disk) (string, error) {
return disk.GetPropertyLocation()
}

// GetDiskPartitionStyle returns the partition style of a disk.
func GetDiskPartitionStyle(disk *storage.MSFT_Disk) (int32, error) {
retValue, err := disk.GetProperty("PartitionStyle")
if err != nil {
return 0, err
}
return retValue.(int32), err
}

// IsDiskOffline returns whether a disk is offline.
func IsDiskOffline(disk *storage.MSFT_Disk) (bool, error) {
return disk.GetPropertyIsOffline()
}

// GetDiskSize returns the size of a disk.
func GetDiskSize(disk *storage.MSFT_Disk) (int64, error) {
sz, err := disk.GetProperty("Size")
if err != nil {
return -1, err
}
return strconv.ParseInt(sz.(string), 10, 64)
}

// GetDiskPath returns the path of a disk.
func GetDiskPath(disk *storage.MSFT_Disk) (string, error) {
return disk.GetPropertyPath()
}

// GetDiskSerialNumber returns the serial number of a disk.
func GetDiskSerialNumber(disk *storage.MSFT_Disk) (string, error) {
return disk.GetPropertySerialNumber()
}
Loading