Skip to content

Commit fa2a268

Browse files
committed
Add Go wrappers for Get/SetVirtualDiskInformation win32 APIs
SetVirtualDiskInformation API is required for running confidential windows containers. The VHDs used for starting confidential pods/UVMs need to have a specific disk identifier. These newly added APIs will be used when preparing the VHDs for confidential pods. This also updates the go module to 1.23 to use binary.Encode method. Signed-off-by: Amit <[email protected]>
1 parent bdc6c11 commit fa2a268

File tree

4 files changed

+184
-1
lines changed

4 files changed

+184
-1
lines changed

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
module github.com/Microsoft/go-winio
22

3-
go 1.21
3+
go 1.23
44

55
require (
66
github.com/sirupsen/logrus v1.9.3

vhd/vhd.go

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,12 @@
44
package vhd
55

66
import (
7+
"bytes"
8+
"encoding/binary"
79
"fmt"
10+
"strings"
811
"syscall"
12+
"unsafe"
913

1014
"github.com/Microsoft/go-winio/pkg/guid"
1115
"golang.org/x/sys/windows"
@@ -18,6 +22,8 @@ import (
1822
//sys attachVirtualDisk(handle syscall.Handle, securityDescriptor *uintptr, attachVirtualDiskFlag uint32, providerSpecificFlags uint32, parameters *AttachVirtualDiskParameters, overlapped *syscall.Overlapped) (win32err error) = virtdisk.AttachVirtualDisk
1923
//sys detachVirtualDisk(handle syscall.Handle, detachVirtualDiskFlags uint32, providerSpecificFlags uint32) (win32err error) = virtdisk.DetachVirtualDisk
2024
//sys getVirtualDiskPhysicalPath(handle syscall.Handle, diskPathSizeInBytes *uint32, buffer *uint16) (win32err error) = virtdisk.GetVirtualDiskPhysicalPath
25+
//sys getVirtualDiskInformation(handle syscall.Handle, bufferSize *uint32, info *virtualDiskInfo, sizeUsed *uint32) (win32err error) = virtdisk.GetVirtualDiskInformation
26+
//sys setVirtualDiskInformation(handle syscall.Handle, info *virtualDiskInfo) (win32err error) = virtdisk.SetVirtualDiskInformation
2127

2228
type (
2329
CreateVirtualDiskFlag uint32
@@ -86,6 +92,20 @@ type AttachVirtualDiskParameters struct {
8692
Version2 AttachVersion2
8793
}
8894

95+
// `virtualDiskInfo` struct is used to represent both GET_VIRTUAL_DISK_INFO
96+
// (https://learn.microsoft.com/en-us/windows/win32/api/virtdisk/ns-virtdisk-get_virtual_disk_info)
97+
// and SET_VIRTUAL_DISK_INFO
98+
// (https://learn.microsoft.com/en-us/windows/win32/api/virtdisk/ns-virtdisk-set_virtual_disk_info)
99+
// win32 types. Both of these win32 types have the same size and a very similar
100+
// structure. These types use tagged unions which aren't directly supported in Go, so we
101+
// keep this type unexported, and provide a cleaner interface to our callers by parsing
102+
// the data buffer for the right type.
103+
type virtualDiskInfo struct {
104+
version uint32
105+
_ [4]byte // padding
106+
data [24]byte // union of various types
107+
}
108+
89109
const (
90110
//revive:disable-next-line:var-naming ALL_CAPS
91111
VIRTUAL_STORAGE_TYPE_DEVICE_VHDX = 0x3
@@ -143,6 +163,34 @@ const (
143163

144164
// Flags for detaching a VHD.
145165
DetachVirtualDiskFlagNone DetachVirtualDiskFlag = 0x0
166+
167+
// Flags for setting information about a VHD - these should remain unexported as we provide APIs to directly get/set a particular field.
168+
setVirtualDiskInfoUnspecified uint32 = 0x0
169+
setVirtualDiskInfoParentPath uint32 = 0x1
170+
setVirtualDiskInfoIdentifier uint32 = 0x2
171+
setVirtualDiskInfoParentPathWithDepth uint32 = 0x3
172+
setVirtualDiskInfoPhysicalSectorSize uint32 = 0x4
173+
setVirtualDiskInfoVirtualDiskId uint32 = 0x5
174+
setVirtualDiskInfoChangeTrackingState uint32 = 0x6
175+
setVirtualDiskInfoParentLocator uint32 = 0x7
176+
177+
// Flags for getting information about a VHD - these should remain unexported as we provide APIs to directly get/set a particular field.
178+
getVirtualDiskInfoUnspecified uint32 = 0x0
179+
getVirtualDiskInfoSize uint32 = 0x1
180+
getVirtualDiskInfoIdentifier uint32 = 0x2
181+
getVirtualDiskInfoParentLocation uint32 = 0x3
182+
getVirtualDiskInfoParentIdentifier uint32 = 0x4
183+
getVirtualDiskInfoParentTimestamp uint32 = 0x5
184+
getVirtualDiskInfoVirtualStorageType uint32 = 0x6
185+
getVirtualDiskInfoProviderSubtype uint32 = 0x7
186+
getVirtualDiskInfoIs4kAligned uint32 = 0x8
187+
getVirtualDiskInfoPhysicalDisk uint32 = 0x9
188+
getVirtualDiskInfoVhdPhysicalSectorSize uint32 = 0xA
189+
getVirtualDiskInfoSmallestSafeVirtualSize uint32 = 0xB
190+
getVirtualDiskInfoFragmentation uint32 = 0xC
191+
getVirtualDiskInfoIsLoaded uint32 = 0xD
192+
getVirtualDiskInfoVirtualDiskId uint32 = 0xE
193+
getVirtualDiskInfoChangeTrackingState uint32 = 0xF
146194
)
147195

148196
// CreateVhdx is a helper function to create a simple vhdx file at the given path using
@@ -375,3 +423,60 @@ func CreateDiffVhd(diffVhdPath, baseVhdPath string, blockSizeInMB uint32) error
375423
}
376424
return nil
377425
}
426+
427+
// SetVirtualDiskIdentifier sets the virtual disk identifier for the specified virtual disk.
428+
func SetVirtualDiskIdentifier(vhdPath string, identifier guid.GUID) error {
429+
handle, err := OpenVirtualDisk(vhdPath, VirtualDiskAccessNone, OpenVirtualDiskFlagNone)
430+
if err != nil {
431+
return fmt.Errorf("failed to open %s: %w", vhdPath, err)
432+
}
433+
defer syscall.Close(handle)
434+
435+
info := &virtualDiskInfo{
436+
version: setVirtualDiskInfoIdentifier,
437+
}
438+
if strings.HasSuffix(vhdPath, ".vhdx") {
439+
// VHDx requires a different version to set disk id
440+
info.version = setVirtualDiskInfoVirtualDiskId
441+
}
442+
443+
if _, err := binary.Encode(info.data[:], binary.LittleEndian, identifier); err != nil {
444+
return fmt.Errorf("failed to serialize virtual disk identifier: %w", err)
445+
}
446+
447+
if err := setVirtualDiskInformation(handle, info); err != nil {
448+
return fmt.Errorf("failed to set virtual disk identifier: %w", err)
449+
}
450+
return nil
451+
}
452+
453+
// GetVirtualDiskIdentifier retrieves the virtual disk identifier for the specified virtual disk.
454+
func GetVirtualDiskIdentifier(vhdPath string) (guid.GUID, error) {
455+
handle, err := OpenVirtualDisk(vhdPath, VirtualDiskAccessNone, OpenVirtualDiskFlagNone)
456+
if err != nil {
457+
return guid.GUID{}, fmt.Errorf("failed to open %s: %w", vhdPath, err)
458+
}
459+
defer syscall.Close(handle)
460+
461+
info := &virtualDiskInfo{
462+
version: getVirtualDiskInfoVirtualDiskId,
463+
}
464+
if strings.HasSuffix(vhdPath, ".vhdx") {
465+
// VHDx requires a different version to get disk id
466+
info.version = getVirtualDiskInfoVirtualDiskId
467+
}
468+
469+
var sizeUsed uint32
470+
bufferSize := uint32(unsafe.Sizeof(*info))
471+
if err := getVirtualDiskInformation(handle, &bufferSize, info, &sizeUsed); err != nil {
472+
return guid.GUID{}, fmt.Errorf("failed to get virtual disk identifier: %w", err)
473+
}
474+
475+
// Parse the response
476+
id := &guid.GUID{}
477+
reader := bytes.NewReader(info.data[:])
478+
if err := binary.Read(reader, binary.LittleEndian, id); err != nil {
479+
return guid.GUID{}, fmt.Errorf("failed to parse virtual disk identifier: %w", err)
480+
}
481+
return *id, nil
482+
}

vhd/vhd_test.go

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
//go:build windows
2+
// +build windows
3+
4+
package vhd
5+
6+
import (
7+
"os"
8+
"path/filepath"
9+
"testing"
10+
11+
"github.com/Microsoft/go-winio/pkg/guid"
12+
)
13+
14+
func TestVirtualDiskIdentifier(t *testing.T) {
15+
// Create a temporary directory for the test
16+
tempDir := t.TempDir()
17+
// TODO(ambarve): We should add a test for VHD too, but our current create VHD API
18+
// seem to only work for VHDX.
19+
vhdPath := filepath.Join(tempDir, "test.vhdx")
20+
21+
// Create the virtual disk
22+
if err := CreateVhdx(vhdPath, 1, 1); err != nil { // 1GB, 1MB block size
23+
t.Fatalf("failed to create virtual disk: %s", err)
24+
}
25+
defer os.Remove(vhdPath)
26+
27+
// Get the initial identifier
28+
initialID, err := GetVirtualDiskIdentifier(vhdPath)
29+
if err != nil {
30+
t.Fatalf("failed to get initial virtual disk identifier: %s", err)
31+
}
32+
t.Logf("initial identifier: %s", initialID.String())
33+
34+
// Create a new GUID to set
35+
newID := guid.GUID{
36+
Data1: 0x12345678,
37+
Data2: 0x1234,
38+
Data3: 0x5678,
39+
Data4: [8]byte{0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0},
40+
}
41+
t.Logf("setting new identifier: %s", newID.String())
42+
43+
// Set the new identifier
44+
if err := SetVirtualDiskIdentifier(vhdPath, newID); err != nil {
45+
t.Fatalf("failed to set virtual disk identifier: %s", err)
46+
}
47+
48+
// Get the identifier again to verify it was set correctly
49+
retrievedID, err := GetVirtualDiskIdentifier(vhdPath)
50+
if err != nil {
51+
t.Fatalf("failed to get virtual disk identifier after setting: %s", err)
52+
}
53+
t.Logf("retrieved identifier: %s", retrievedID.String())
54+
55+
// Verify the retrieved ID matches the one we set
56+
if retrievedID != newID {
57+
t.Errorf("retrieved identifier does not match set identifier.\nExpected: %s\nGot: %s",
58+
newID.String(), retrievedID.String())
59+
}
60+
}

vhd/zvhd_windows.go

Lines changed: 18 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)