1
2
3
4 package vhd
5
6 import (
7 "fmt"
8 "syscall"
9
10 "github.com/Microsoft/go-winio/pkg/guid"
11 "golang.org/x/sys/windows"
12 )
13
14
15
16
17
18
19
20
21
22 type (
23 CreateVirtualDiskFlag uint32
24 VirtualDiskFlag uint32
25 AttachVirtualDiskFlag uint32
26 DetachVirtualDiskFlag uint32
27 VirtualDiskAccessMask uint32
28 )
29
30 type VirtualStorageType struct {
31 DeviceID uint32
32 VendorID guid.GUID
33 }
34
35 type CreateVersion2 struct {
36 UniqueID guid.GUID
37 MaximumSize uint64
38 BlockSizeInBytes uint32
39 SectorSizeInBytes uint32
40 PhysicalSectorSizeInByte uint32
41 ParentPath *uint16
42 SourcePath *uint16
43 OpenFlags uint32
44 ParentVirtualStorageType VirtualStorageType
45 SourceVirtualStorageType VirtualStorageType
46 ResiliencyGUID guid.GUID
47 }
48
49 type CreateVirtualDiskParameters struct {
50 Version uint32
51 Version2 CreateVersion2
52 }
53
54 type OpenVersion2 struct {
55 GetInfoOnly bool
56 ReadOnly bool
57 ResiliencyGUID guid.GUID
58 }
59
60 type OpenVirtualDiskParameters struct {
61 Version uint32
62 Version2 OpenVersion2
63 }
64
65
66
67
68 type openVersion2 struct {
69 getInfoOnly int32
70 readOnly int32
71 resiliencyGUID guid.GUID
72 }
73
74 type openVirtualDiskParameters struct {
75 version uint32
76 version2 openVersion2
77 }
78
79 type AttachVersion2 struct {
80 RestrictedOffset uint64
81 RestrictedLength uint64
82 }
83
84 type AttachVirtualDiskParameters struct {
85 Version uint32
86 Version2 AttachVersion2
87 }
88
89 const (
90
91 VIRTUAL_STORAGE_TYPE_DEVICE_VHDX = 0x3
92
93
94 VirtualDiskAccessNone VirtualDiskAccessMask = 0x00000000
95 VirtualDiskAccessAttachRO VirtualDiskAccessMask = 0x00010000
96 VirtualDiskAccessAttachRW VirtualDiskAccessMask = 0x00020000
97 VirtualDiskAccessDetach VirtualDiskAccessMask = 0x00040000
98 VirtualDiskAccessGetInfo VirtualDiskAccessMask = 0x00080000
99 VirtualDiskAccessCreate VirtualDiskAccessMask = 0x00100000
100 VirtualDiskAccessMetaOps VirtualDiskAccessMask = 0x00200000
101 VirtualDiskAccessRead VirtualDiskAccessMask = 0x000d0000
102 VirtualDiskAccessAll VirtualDiskAccessMask = 0x003f0000
103 VirtualDiskAccessWritable VirtualDiskAccessMask = 0x00320000
104
105
106 CreateVirtualDiskFlagNone CreateVirtualDiskFlag = 0x0
107 CreateVirtualDiskFlagFullPhysicalAllocation CreateVirtualDiskFlag = 0x1
108 CreateVirtualDiskFlagPreventWritesToSourceDisk CreateVirtualDiskFlag = 0x2
109 CreateVirtualDiskFlagDoNotCopyMetadataFromParent CreateVirtualDiskFlag = 0x4
110 CreateVirtualDiskFlagCreateBackingStorage CreateVirtualDiskFlag = 0x8
111 CreateVirtualDiskFlagUseChangeTrackingSourceLimit CreateVirtualDiskFlag = 0x10
112 CreateVirtualDiskFlagPreserveParentChangeTrackingState CreateVirtualDiskFlag = 0x20
113 CreateVirtualDiskFlagVhdSetUseOriginalBackingStorage CreateVirtualDiskFlag = 0x40
114 CreateVirtualDiskFlagSparseFile CreateVirtualDiskFlag = 0x80
115 CreateVirtualDiskFlagPmemCompatible CreateVirtualDiskFlag = 0x100
116 CreateVirtualDiskFlagSupportCompressedVolumes CreateVirtualDiskFlag = 0x200
117
118
119 OpenVirtualDiskFlagNone VirtualDiskFlag = 0x00000000
120 OpenVirtualDiskFlagNoParents VirtualDiskFlag = 0x00000001
121 OpenVirtualDiskFlagBlankFile VirtualDiskFlag = 0x00000002
122 OpenVirtualDiskFlagBootDrive VirtualDiskFlag = 0x00000004
123 OpenVirtualDiskFlagCachedIO VirtualDiskFlag = 0x00000008
124 OpenVirtualDiskFlagCustomDiffChain VirtualDiskFlag = 0x00000010
125 OpenVirtualDiskFlagParentCachedIO VirtualDiskFlag = 0x00000020
126 OpenVirtualDiskFlagVhdsetFileOnly VirtualDiskFlag = 0x00000040
127 OpenVirtualDiskFlagIgnoreRelativeParentLocator VirtualDiskFlag = 0x00000080
128 OpenVirtualDiskFlagNoWriteHardening VirtualDiskFlag = 0x00000100
129 OpenVirtualDiskFlagSupportCompressedVolumes VirtualDiskFlag = 0x00000200
130
131
132 AttachVirtualDiskFlagNone AttachVirtualDiskFlag = 0x00000000
133 AttachVirtualDiskFlagReadOnly AttachVirtualDiskFlag = 0x00000001
134 AttachVirtualDiskFlagNoDriveLetter AttachVirtualDiskFlag = 0x00000002
135 AttachVirtualDiskFlagPermanentLifetime AttachVirtualDiskFlag = 0x00000004
136 AttachVirtualDiskFlagNoLocalHost AttachVirtualDiskFlag = 0x00000008
137 AttachVirtualDiskFlagNoSecurityDescriptor AttachVirtualDiskFlag = 0x00000010
138 AttachVirtualDiskFlagBypassDefaultEncryptionPolicy AttachVirtualDiskFlag = 0x00000020
139 AttachVirtualDiskFlagNonPnp AttachVirtualDiskFlag = 0x00000040
140 AttachVirtualDiskFlagRestrictedRange AttachVirtualDiskFlag = 0x00000080
141 AttachVirtualDiskFlagSinglePartition AttachVirtualDiskFlag = 0x00000100
142 AttachVirtualDiskFlagRegisterVolume AttachVirtualDiskFlag = 0x00000200
143
144
145 DetachVirtualDiskFlagNone DetachVirtualDiskFlag = 0x0
146 )
147
148
149
150
151
152 func CreateVhdx(path string, maxSizeInGb, blockSizeInMb uint32) error {
153 params := CreateVirtualDiskParameters{
154 Version: 2,
155 Version2: CreateVersion2{
156 MaximumSize: uint64(maxSizeInGb) * 1024 * 1024 * 1024,
157 BlockSizeInBytes: blockSizeInMb * 1024 * 1024,
158 },
159 }
160
161 handle, err := CreateVirtualDisk(path, VirtualDiskAccessNone, CreateVirtualDiskFlagNone, ¶ms)
162 if err != nil {
163 return err
164 }
165
166 return syscall.CloseHandle(handle)
167 }
168
169
170 func DetachVirtualDisk(handle syscall.Handle) (err error) {
171 if err := detachVirtualDisk(handle, 0, 0); err != nil {
172 return fmt.Errorf("failed to detach virtual disk: %w", err)
173 }
174 return nil
175 }
176
177
178
179
180 func DetachVhd(path string) error {
181 handle, err := OpenVirtualDisk(
182 path,
183 VirtualDiskAccessNone,
184 OpenVirtualDiskFlagCachedIO|OpenVirtualDiskFlagIgnoreRelativeParentLocator,
185 )
186 if err != nil {
187 return err
188 }
189 defer syscall.CloseHandle(handle)
190 return DetachVirtualDisk(handle)
191 }
192
193
194 func AttachVirtualDisk(
195 handle syscall.Handle,
196 attachVirtualDiskFlag AttachVirtualDiskFlag,
197 parameters *AttachVirtualDiskParameters,
198 ) (err error) {
199
200 if err := attachVirtualDisk(
201 handle,
202 nil,
203 uint32(attachVirtualDiskFlag),
204 0,
205 parameters,
206 nil,
207 ); err != nil {
208 return fmt.Errorf("failed to attach virtual disk: %w", err)
209 }
210 return nil
211 }
212
213
214
215
216
217 func AttachVhd(path string) (err error) {
218 handle, err := OpenVirtualDisk(
219 path,
220 VirtualDiskAccessNone,
221 OpenVirtualDiskFlagCachedIO|OpenVirtualDiskFlagIgnoreRelativeParentLocator,
222 )
223 if err != nil {
224 return err
225 }
226
227 defer syscall.CloseHandle(handle)
228 params := AttachVirtualDiskParameters{Version: 2}
229 if err := AttachVirtualDisk(
230 handle,
231 AttachVirtualDiskFlagNone,
232 ¶ms,
233 ); err != nil {
234 return fmt.Errorf("failed to attach virtual disk: %w", err)
235 }
236 return nil
237 }
238
239
240 func OpenVirtualDisk(
241 vhdPath string,
242 virtualDiskAccessMask VirtualDiskAccessMask,
243 openVirtualDiskFlags VirtualDiskFlag,
244 ) (syscall.Handle, error) {
245 parameters := OpenVirtualDiskParameters{Version: 2}
246 handle, err := OpenVirtualDiskWithParameters(
247 vhdPath,
248 virtualDiskAccessMask,
249 openVirtualDiskFlags,
250 ¶meters,
251 )
252 if err != nil {
253 return 0, err
254 }
255 return handle, nil
256 }
257
258
259 func OpenVirtualDiskWithParameters(
260 vhdPath string,
261 virtualDiskAccessMask VirtualDiskAccessMask,
262 openVirtualDiskFlags VirtualDiskFlag,
263 parameters *OpenVirtualDiskParameters,
264 ) (syscall.Handle, error) {
265 var (
266 handle syscall.Handle
267 defaultType VirtualStorageType
268 getInfoOnly int32
269 readOnly int32
270 )
271 if parameters.Version != 2 {
272 return handle, fmt.Errorf("only version 2 VHDs are supported, found version: %d", parameters.Version)
273 }
274 if parameters.Version2.GetInfoOnly {
275 getInfoOnly = 1
276 }
277 if parameters.Version2.ReadOnly {
278 readOnly = 1
279 }
280 params := &openVirtualDiskParameters{
281 version: parameters.Version,
282 version2: openVersion2{
283 getInfoOnly,
284 readOnly,
285 parameters.Version2.ResiliencyGUID,
286 },
287 }
288 if err := openVirtualDisk(
289 &defaultType,
290 vhdPath,
291 uint32(virtualDiskAccessMask),
292 uint32(openVirtualDiskFlags),
293 params,
294 &handle,
295 ); err != nil {
296 return 0, fmt.Errorf("failed to open virtual disk: %w", err)
297 }
298 return handle, nil
299 }
300
301
302 func CreateVirtualDisk(
303 path string,
304 virtualDiskAccessMask VirtualDiskAccessMask,
305 createVirtualDiskFlags CreateVirtualDiskFlag,
306 parameters *CreateVirtualDiskParameters,
307 ) (syscall.Handle, error) {
308 var (
309 handle syscall.Handle
310 defaultType VirtualStorageType
311 )
312 if parameters.Version != 2 {
313 return handle, fmt.Errorf("only version 2 VHDs are supported, found version: %d", parameters.Version)
314 }
315
316 if err := createVirtualDisk(
317 &defaultType,
318 path,
319 uint32(virtualDiskAccessMask),
320 nil,
321 uint32(createVirtualDiskFlags),
322 0,
323 parameters,
324 nil,
325 &handle,
326 ); err != nil {
327 return handle, fmt.Errorf("failed to create virtual disk: %w", err)
328 }
329 return handle, nil
330 }
331
332
333
334
335 func GetVirtualDiskPhysicalPath(handle syscall.Handle) (_ string, err error) {
336 var (
337 diskPathSizeInBytes uint32 = 256 * 2
338 diskPhysicalPathBuf [256]uint16
339 )
340 if err := getVirtualDiskPhysicalPath(
341 handle,
342 &diskPathSizeInBytes,
343 &diskPhysicalPathBuf[0],
344 ); err != nil {
345 return "", fmt.Errorf("failed to get disk physical path: %w", err)
346 }
347 return windows.UTF16ToString(diskPhysicalPathBuf[:]), nil
348 }
349
350
351
352
353 func CreateDiffVhd(diffVhdPath, baseVhdPath string, blockSizeInMB uint32) error {
354
355 createParams := &CreateVirtualDiskParameters{
356 Version: 2,
357 Version2: CreateVersion2{
358 ParentPath: windows.StringToUTF16Ptr(baseVhdPath),
359 BlockSizeInBytes: blockSizeInMB * 1024 * 1024,
360 OpenFlags: uint32(OpenVirtualDiskFlagCachedIO),
361 },
362 }
363
364 vhdHandle, err := CreateVirtualDisk(
365 diffVhdPath,
366 VirtualDiskAccessNone,
367 CreateVirtualDiskFlagNone,
368 createParams,
369 )
370 if err != nil {
371 return fmt.Errorf("failed to create differencing vhd: %w", err)
372 }
373 if err := syscall.CloseHandle(vhdHandle); err != nil {
374 return fmt.Errorf("failed to close differencing vhd handle: %w", err)
375 }
376 return nil
377 }
378
View as plain text