...

Source file src/github.com/Microsoft/go-winio/vhd/vhd.go

Documentation: github.com/Microsoft/go-winio/vhd

     1  //go:build windows
     2  // +build windows
     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  //go:generate go run github.com/Microsoft/go-winio/tools/mkwinsyscall -output zvhd_windows.go vhd.go
    15  
    16  //sys createVirtualDisk(virtualStorageType *VirtualStorageType, path string, virtualDiskAccessMask uint32, securityDescriptor *uintptr, createVirtualDiskFlags uint32, providerSpecificFlags uint32, parameters *CreateVirtualDiskParameters, overlapped *syscall.Overlapped, handle *syscall.Handle) (win32err error) = virtdisk.CreateVirtualDisk
    17  //sys openVirtualDisk(virtualStorageType *VirtualStorageType, path string, virtualDiskAccessMask uint32, openVirtualDiskFlags uint32, parameters *openVirtualDiskParameters, handle *syscall.Handle) (win32err error) = virtdisk.OpenVirtualDisk
    18  //sys attachVirtualDisk(handle syscall.Handle, securityDescriptor *uintptr, attachVirtualDiskFlag uint32, providerSpecificFlags uint32, parameters *AttachVirtualDiskParameters, overlapped *syscall.Overlapped) (win32err error) = virtdisk.AttachVirtualDisk
    19  //sys detachVirtualDisk(handle syscall.Handle, detachVirtualDiskFlags uint32, providerSpecificFlags uint32) (win32err error) = virtdisk.DetachVirtualDisk
    20  //sys getVirtualDiskPhysicalPath(handle syscall.Handle, diskPathSizeInBytes *uint32, buffer *uint16) (win32err error) = virtdisk.GetVirtualDiskPhysicalPath
    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 // string
    42  	SourcePath               *uint16 // string
    43  	OpenFlags                uint32
    44  	ParentVirtualStorageType VirtualStorageType
    45  	SourceVirtualStorageType VirtualStorageType
    46  	ResiliencyGUID           guid.GUID
    47  }
    48  
    49  type CreateVirtualDiskParameters struct {
    50  	Version  uint32 // Must always be set to 2
    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 // Must always be set to 2
    62  	Version2 OpenVersion2
    63  }
    64  
    65  // The higher level `OpenVersion2` struct uses `bool`s to refer to `GetInfoOnly` and `ReadOnly` for ease of use. However,
    66  // the internal windows structure uses `BOOL`s aka int32s for these types. `openVersion2` is used for translating
    67  // `OpenVersion2` fields to the correct windows internal field types on the `Open____` methods.
    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  	//revive:disable-next-line:var-naming ALL_CAPS
    91  	VIRTUAL_STORAGE_TYPE_DEVICE_VHDX = 0x3
    92  
    93  	// Access Mask for opening a VHD.
    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  	// Flags for creating a VHD.
   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 //revive:disable-line:var-naming VHD, not Vhd
   114  	CreateVirtualDiskFlagSparseFile                        CreateVirtualDiskFlag = 0x80
   115  	CreateVirtualDiskFlagPmemCompatible                    CreateVirtualDiskFlag = 0x100 //revive:disable-line:var-naming PMEM, not Pmem
   116  	CreateVirtualDiskFlagSupportCompressedVolumes          CreateVirtualDiskFlag = 0x200
   117  
   118  	// Flags for opening a VHD.
   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  	// Flags for attaching a VHD.
   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  	// Flags for detaching a VHD.
   145  	DetachVirtualDiskFlagNone DetachVirtualDiskFlag = 0x0
   146  )
   147  
   148  // CreateVhdx is a helper function to create a simple vhdx file at the given path using
   149  // default values.
   150  //
   151  //revive:disable-next-line:var-naming VHDX, not Vhdx
   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, &params)
   162  	if err != nil {
   163  		return err
   164  	}
   165  
   166  	return syscall.CloseHandle(handle)
   167  }
   168  
   169  // DetachVirtualDisk detaches a virtual hard disk by handle.
   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  // DetachVhd detaches a vhd found at `path`.
   178  //
   179  //revive:disable-next-line:var-naming VHD, not Vhd
   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) //nolint:errcheck
   190  	return DetachVirtualDisk(handle)
   191  }
   192  
   193  // AttachVirtualDisk attaches a virtual hard disk for use.
   194  func AttachVirtualDisk(
   195  	handle syscall.Handle,
   196  	attachVirtualDiskFlag AttachVirtualDiskFlag,
   197  	parameters *AttachVirtualDiskParameters,
   198  ) (err error) {
   199  	// Supports both version 1 and 2 of the attach parameters as version 2 wasn't present in RS5.
   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  // AttachVhd attaches a virtual hard disk at `path` for use. Attaches using version 2
   214  // of the ATTACH_VIRTUAL_DISK_PARAMETERS.
   215  //
   216  //revive:disable-next-line:var-naming VHD, not Vhd
   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) //nolint:errcheck
   228  	params := AttachVirtualDiskParameters{Version: 2}
   229  	if err := AttachVirtualDisk(
   230  		handle,
   231  		AttachVirtualDiskFlagNone,
   232  		&params,
   233  	); err != nil {
   234  		return fmt.Errorf("failed to attach virtual disk: %w", err)
   235  	}
   236  	return nil
   237  }
   238  
   239  // OpenVirtualDisk obtains a handle to a VHD opened with supplied access mask and flags.
   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  		&parameters,
   251  	)
   252  	if err != nil {
   253  		return 0, err
   254  	}
   255  	return handle, nil
   256  }
   257  
   258  // OpenVirtualDiskWithParameters obtains a handle to a VHD opened with supplied access mask, flags and parameters.
   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  // CreateVirtualDisk creates a virtual harddisk and returns a handle to the disk.
   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  // GetVirtualDiskPhysicalPath takes a handle to a virtual hard disk and returns the physical
   333  // path of the disk on the machine. This path is in the form \\.\PhysicalDriveX where X is an integer
   334  // that represents the particular enumeration of the physical disk on the caller's system.
   335  func GetVirtualDiskPhysicalPath(handle syscall.Handle) (_ string, err error) {
   336  	var (
   337  		diskPathSizeInBytes uint32 = 256 * 2 // max path length 256 wide chars
   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  // CreateDiffVhd is a helper function to create a differencing virtual disk.
   351  //
   352  //revive:disable-next-line:var-naming VHD, not Vhd
   353  func CreateDiffVhd(diffVhdPath, baseVhdPath string, blockSizeInMB uint32) error {
   354  	// Setting `ParentPath` is how to signal to create a differencing disk.
   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