2022-04-11 17:46:20 -04:00
global using System.Threading ;
global using System.Threading.Tasks ;
2017-12-22 11:09:04 -05:00
using System.Collections.Generic ;
using System.Diagnostics ;
using System.IO ;
2017-11-27 13:11:20 -05:00
using System.Linq ;
2017-12-22 11:09:04 -05:00
using System.Security.AccessControl ;
2017-11-27 13:11:20 -05:00
using Vanara.PInvoke ;
2018-09-04 15:43:41 -04:00
using static Vanara . PInvoke . AdvApi32 ;
2022-04-11 17:46:20 -04:00
using static Vanara . PInvoke . Kernel32 ;
2017-11-27 13:11:20 -05:00
using static Vanara . PInvoke . VirtDisk ;
2022-04-11 17:46:20 -04:00
namespace Vanara.IO ;
/// <summary>Class that represents a virtual disk and allows for performing actions on it. This wraps most of the methods found in virtdisk.h.</summary>
/// <seealso cref="System.IDisposable"/>
[DebuggerDisplay("[{ImagePath} - Attached={Attached}] ")]
public partial class VirtualDisk : IDisposable , IHandle
2017-11-27 13:11:20 -05:00
{
2022-04-11 17:46:20 -04:00
private static readonly bool IsPreWin8 = ! PInvokeClient . Windows8 . IsPlatformSupported ( ) ;
private readonly OPEN_VIRTUAL_DISK_VERSION ver ;
2023-09-06 11:14:25 -04:00
private VirtualDiskMetadata ? metadata ;
2017-11-27 13:11:20 -05:00
2022-04-11 17:46:20 -04:00
private VirtualDisk ( SafeVIRTUAL_DISK_HANDLE handle , OPEN_VIRTUAL_DISK_VERSION version , string imgPath )
{
2023-09-06 11:14:25 -04:00
if ( handle is null | | handle . IsInvalid )
2018-11-19 23:18:50 -05:00
{
2022-04-11 17:46:20 -04:00
throw new ArgumentNullException ( nameof ( handle ) ) ;
2018-11-19 23:18:50 -05:00
}
2017-11-27 13:11:20 -05:00
2022-04-11 17:46:20 -04:00
Handle = handle ;
ImagePath = imgPath ;
ver = version ;
}
2019-10-04 08:55:01 -04:00
2022-04-11 17:46:20 -04:00
private delegate Win32Error RunAsyncMethod ( in NativeOverlapped overlap ) ;
2017-11-27 13:11:20 -05:00
2022-04-11 17:46:20 -04:00
/// <summary>Represents the format of the virtual disk.</summary>
public enum DeviceType : uint
{
/// <summary>Device type is unknown or not valid.</summary>
Unknown = VIRTUAL_STORAGE_TYPE_DEVICE_TYPE . VIRTUAL_STORAGE_TYPE_DEVICE_UNKNOWN ,
2019-10-04 08:55:01 -04:00
2022-04-11 17:46:20 -04:00
/// <summary>
/// CD or DVD image file device type. (.iso file)
/// <para><c>Windows 7 and Windows Server 2008 R2:</c> This value is not supported before Windows 8 and Windows Server 2012.</para>
/// </summary>
Iso = VIRTUAL_STORAGE_TYPE_DEVICE_TYPE . VIRTUAL_STORAGE_TYPE_DEVICE_ISO ,
2019-10-04 08:55:01 -04:00
2022-04-11 17:46:20 -04:00
/// <summary>Virtual hard disk device type. (.vhd file)</summary>
Vhd = VIRTUAL_STORAGE_TYPE_DEVICE_TYPE . VIRTUAL_STORAGE_TYPE_DEVICE_VHD ,
/// <summary>
/// VHDX format virtual hard disk device type. (.vhdx file)
/// <para><c>Windows 7 and Windows Server 2008 R2:</c> This value is not supported before Windows 8 and Windows Server 2012.</para>
/// </summary>
Vhdx = VIRTUAL_STORAGE_TYPE_DEVICE_TYPE . VIRTUAL_STORAGE_TYPE_DEVICE_VHDX ,
/// <summary>
/// VHD Set files (.vhds file) are a new shared Virtual Disk model for guest clusters in Windows Server 2016. VHD Set files support
/// online resizing of shared virtual disks, support Hyper-V Replica, and can be included in application-consistent checkpoints.
/// </summary>
VhdSet = VIRTUAL_STORAGE_TYPE_DEVICE_TYPE . VIRTUAL_STORAGE_TYPE_DEVICE_VHDSET
}
/// <summary>Represents the subtype of a virtual disk.</summary>
public enum Subtype : uint
{
/// <summary>Fixed.</summary>
Fixed = 2 ,
2017-12-22 11:09:04 -05:00
2022-04-11 17:46:20 -04:00
/// <summary>Dynamically expandable (sparse).</summary>
Dynamic = 3 ,
2017-11-27 13:11:20 -05:00
2022-04-11 17:46:20 -04:00
/// <summary>Differencing.</summary>
Differencing = 4
}
2017-11-27 13:11:20 -05:00
2022-04-11 17:46:20 -04:00
/// <summary>Indicates whether this virtual disk is currently attached.</summary>
public bool Attached = > IsAttached ( ImagePath ) ;
2017-11-27 13:11:20 -05:00
2022-04-11 17:46:20 -04:00
/// <summary>Block size of the VHD, in bytes.</summary>
public uint BlockSize = > GetInformation < uint > ( GET_VIRTUAL_DISK_INFO_VERSION . GET_VIRTUAL_DISK_INFO_SIZE , 16 ) ;
2017-11-27 13:11:20 -05:00
2022-04-11 17:46:20 -04:00
/// <summary>The device identifier.</summary>
public DeviceType DiskType = > ( DeviceType ) GetInformation < uint > ( GET_VIRTUAL_DISK_INFO_VERSION . GET_VIRTUAL_DISK_INFO_VIRTUAL_STORAGE_TYPE ) ;
/// <summary>The fragmentation level of the virtual disk.</summary>
public uint? FragmentationPercentage = > IsChild ? null : GetInformation < uint > ( GET_VIRTUAL_DISK_INFO_VERSION . GET_VIRTUAL_DISK_INFO_FRAGMENTATION ) ;
/// <summary>Unique identifier of the VHD.</summary>
public Guid Identifier
{
get = > GetInformation < Guid > ( GET_VIRTUAL_DISK_INFO_VERSION . GET_VIRTUAL_DISK_INFO_IDENTIFIER ) ;
set
2017-11-27 13:11:20 -05:00
{
2022-04-11 17:46:20 -04:00
SET_VIRTUAL_DISK_INFO si = new ( SET_VIRTUAL_DISK_INFO_VERSION . SET_VIRTUAL_DISK_INFO_IDENTIFIER ) { UniqueIdentifier = value } ;
SetVirtualDiskInformation ( Handle , si ) . ThrowIfFailed ( ) ;
2017-11-27 13:11:20 -05:00
}
2022-04-11 17:46:20 -04:00
}
2017-11-27 13:11:20 -05:00
2022-04-11 17:46:20 -04:00
/// <summary>Gets the path of the image file provided when opening or creating this instance.</summary>
/// <value>The image path.</value>
public string ImagePath { get ; private set ; }
2022-04-09 20:40:26 -04:00
2022-04-11 17:46:20 -04:00
/// <summary>Indicates whether the virtual disk is 4 KB aligned.</summary>
public bool Is4kAligned = > GetInformation < bool > ( GET_VIRTUAL_DISK_INFO_VERSION . GET_VIRTUAL_DISK_INFO_IS_4K_ALIGNED ) ;
2017-11-27 13:11:20 -05:00
2022-04-11 17:46:20 -04:00
/// <summary>Gets a value indicating whether this virtual disk has a parent backing store.</summary>
/// <value><see langword="true"/> if this instance has a parent backing store; otherwise, <see langword="false"/>.</value>
public bool IsChild = > ProviderSubtype = = Subtype . Differencing ;
2021-06-01 13:53:58 -04:00
2022-04-11 17:46:20 -04:00
/// <summary>
/// Indicates whether the virtual disk is currently mounted and in use. TRUE if the virtual disk is currently mounted and in use;
/// otherwise FALSE.
/// </summary>
public bool IsLoaded = > GetInformation < bool > ( GET_VIRTUAL_DISK_INFO_VERSION . GET_VIRTUAL_DISK_INFO_IS_LOADED ) ;
2017-11-27 13:11:20 -05:00
2022-04-11 17:46:20 -04:00
/// <summary>Indicates whether the physical disk is remote.</summary>
public bool IsRemote = > GetInformation < bool > ( GET_VIRTUAL_DISK_INFO_VERSION . GET_VIRTUAL_DISK_INFO_PHYSICAL_DISK , 8 ) ;
2017-11-27 13:11:20 -05:00
2022-04-11 17:46:20 -04:00
/// <summary>The logical sector size of the physical disk.</summary>
public uint LogicalSectorSize = > GetInformation < uint > ( GET_VIRTUAL_DISK_INFO_VERSION . GET_VIRTUAL_DISK_INFO_PHYSICAL_DISK ) ;
2017-11-27 13:11:20 -05:00
2022-04-11 17:46:20 -04:00
/// <summary>Gets the metadata associated with this virtual disk. Currently on VHDX files support metadata.</summary>
/// <value>The metadata.</value>
public VirtualDiskMetadata Metadata = > metadata ? ? = new VirtualDiskMetadata ( this ) ;
2017-12-22 11:09:04 -05:00
2022-04-11 17:46:20 -04:00
/// <summary>
/// The change tracking identifier for the change that identifies the state of the virtual disk that you want to use as the basis of
/// comparison to determine whether the NewerChanges member reports new changes.
/// </summary>
public string MostRecentId = > GetInformation < string > ( GET_VIRTUAL_DISK_INFO_VERSION . GET_VIRTUAL_DISK_INFO_CHANGE_TRACKING_STATE , 8 ) ;
2017-11-27 13:11:20 -05:00
2022-04-11 17:46:20 -04:00
/// <summary>
/// Whether the virtual disk has changed since the change identified by the MostRecentId member occurred. TRUE if the virtual disk has
/// changed since the change identified by the MostRecentId member occurred; otherwise FALSE.
/// </summary>
public bool NewerChanges = > GetInformation < bool > ( GET_VIRTUAL_DISK_INFO_VERSION . GET_VIRTUAL_DISK_INFO_CHANGE_TRACKING_STATE , 4 ) ;
2017-11-27 13:11:20 -05:00
2022-04-11 17:46:20 -04:00
/// <summary>The path of the parent backing store, if it can be resolved.</summary>
2023-09-06 11:14:25 -04:00
public string? ParentBackingStore = > IsChild ? GetInformation < string > ( GET_VIRTUAL_DISK_INFO_VERSION . GET_VIRTUAL_DISK_INFO_PARENT_LOCATION , 4 ) : null ;
2017-11-27 13:11:20 -05:00
2022-04-11 17:46:20 -04:00
/// <summary>Unique identifier of the parent disk backing store. If unattached, <see langword="null"/> is returned.</summary>
public Guid ? ParentIdentifier = > IsChild ? GetInformation < Guid > ( GET_VIRTUAL_DISK_INFO_VERSION . GET_VIRTUAL_DISK_INFO_PARENT_IDENTIFIER ) : null ;
2017-11-27 13:11:20 -05:00
2022-04-11 17:46:20 -04:00
/// <summary>
/// If this is a child, contains the path of the parent backing store. If is a parent disk, contains all of the parent paths present in
/// the search list. If unattached, <see langword="null"/> is returned.
/// </summary>
2023-09-06 11:14:25 -04:00
public string [ ] ? ParentPaths
2022-04-11 17:46:20 -04:00
{
get
2017-11-27 13:11:20 -05:00
{
2022-04-11 17:46:20 -04:00
if ( IsChild )
2017-11-27 13:11:20 -05:00
{
2022-04-11 17:46:20 -04:00
var pPath = GetInformation < string > ( GET_VIRTUAL_DISK_INFO_VERSION . GET_VIRTUAL_DISK_INFO_PARENT_LOCATION , 4 ) ;
return ! string . IsNullOrEmpty ( pPath ) ? new [ ] { pPath } : null ;
}
if ( Attached )
{
return GetInformation < string [ ] > ( GET_VIRTUAL_DISK_INFO_VERSION . GET_VIRTUAL_DISK_INFO_PARENT_LOCATION , 4 ) ;
2017-11-27 13:11:20 -05:00
}
2022-04-11 17:46:20 -04:00
return null ;
2017-11-27 13:11:20 -05:00
}
2022-04-11 17:46:20 -04:00
}
2017-11-27 13:11:20 -05:00
2022-04-11 17:46:20 -04:00
/// <summary>Internal time stamp of the parent disk backing store. If unattached, <see langword="null"/> is returned.</summary>
public uint? ParentTimeStamp = > IsChild ? GetInformation < uint > ( GET_VIRTUAL_DISK_INFO_VERSION . GET_VIRTUAL_DISK_INFO_PARENT_TIMESTAMP ) : null ;
2017-11-27 13:11:20 -05:00
2022-04-11 17:46:20 -04:00
/// <summary>
/// Retrieves the path to the physical device object that contains a virtual hard disk (VHD) or CD or DVD image file (ISO). If
/// unattached, <see langword="null"/> is returned.
/// </summary>
2023-09-06 11:14:25 -04:00
public string? PhysicalPath
2022-04-11 17:46:20 -04:00
{
get
2017-11-27 13:11:20 -05:00
{
2022-04-11 17:46:20 -04:00
if ( ! Attached )
return null ;
2022-04-09 20:40:26 -04:00
2022-04-11 17:46:20 -04:00
var sz = 64 ;
StringBuilder sb = new ( sz ) ;
Win32Error err ;
do
{
err = GetVirtualDiskPhysicalPath ( Handle , ref sz , sb ) ;
if ( err = = Win32Error . ERROR_INSUFFICIENT_BUFFER )
sb . Capacity * = 4 ;
} while ( err = = Win32Error . ERROR_INSUFFICIENT_BUFFER ) ;
err . ThrowIfFailed ( ) ;
return sb . ToString ( ) ;
2017-11-27 13:11:20 -05:00
}
2022-04-11 17:46:20 -04:00
}
2017-11-27 13:11:20 -05:00
2022-04-11 17:46:20 -04:00
/// <summary>The physical sector size of the physical disk.</summary>
public uint PhysicalSectorSize
{
get = > GetInformation < uint > ( GET_VIRTUAL_DISK_INFO_VERSION . GET_VIRTUAL_DISK_INFO_PHYSICAL_DISK , 4 ) ;
set
2017-11-27 13:11:20 -05:00
{
2022-04-11 17:46:20 -04:00
SET_VIRTUAL_DISK_INFO si = new ( SET_VIRTUAL_DISK_INFO_VERSION . SET_VIRTUAL_DISK_INFO_PHYSICAL_SECTOR_SIZE ) { VhdPhysicalSectorSize = value } ;
SetVirtualDiskInformation ( Handle , si ) . ThrowIfFailed ( ) ;
2017-11-27 13:11:20 -05:00
}
2022-04-11 17:46:20 -04:00
}
2017-11-27 13:11:20 -05:00
2022-04-11 17:46:20 -04:00
/// <summary>Physical size of the VHD on disk, in bytes.</summary>
public ulong PhysicalSize = > GetInformation < ulong > ( GET_VIRTUAL_DISK_INFO_VERSION . GET_VIRTUAL_DISK_INFO_SIZE , 8 ) ;
2017-11-27 13:11:20 -05:00
2022-04-11 17:46:20 -04:00
/// <summary>Provider-specific subtype.</summary>
public Subtype ProviderSubtype = > ( Subtype ) GetInformation < uint > ( GET_VIRTUAL_DISK_INFO_VERSION . GET_VIRTUAL_DISK_INFO_PROVIDER_SUBTYPE ) ;
2017-11-27 13:11:20 -05:00
2022-04-11 17:46:20 -04:00
/// <summary>Whether RCT is turned on. TRUE if RCT is turned on; otherwise FALSE.</summary>
public bool ResilientChangeTrackingEnabled
{
get = > GetInformation < bool > ( GET_VIRTUAL_DISK_INFO_VERSION . GET_VIRTUAL_DISK_INFO_CHANGE_TRACKING_STATE ) ;
set
2021-05-31 23:45:26 -04:00
{
2022-04-11 17:46:20 -04:00
SET_VIRTUAL_DISK_INFO si = new ( SET_VIRTUAL_DISK_INFO_VERSION . SET_VIRTUAL_DISK_INFO_CHANGE_TRACKING_STATE ) { ChangeTrackingEnabled = value } ;
SetVirtualDiskInformation ( Handle , si ) . ThrowIfFailed ( ) ;
2021-05-31 23:45:26 -04:00
}
2022-04-11 17:46:20 -04:00
}
2021-05-31 23:45:26 -04:00
2022-04-11 17:46:20 -04:00
/// <summary>Sector size of the VHD, in bytes.</summary>
public uint SectorSize = > GetInformation < uint > ( GET_VIRTUAL_DISK_INFO_VERSION . GET_VIRTUAL_DISK_INFO_SIZE , 20 ) ;
2017-11-27 13:11:20 -05:00
2022-04-11 17:46:20 -04:00
/// <summary>The smallest safe minimum size of the virtual disk. If unattached, <see langword="null"/> is returned.</summary>
public ulong? SmallestSafeVirtualSize = > Attached ? GetInformation < ulong > ( GET_VIRTUAL_DISK_INFO_VERSION . GET_VIRTUAL_DISK_INFO_SMALLEST_SAFE_VIRTUAL_SIZE ) : null ;
2017-11-27 13:11:20 -05:00
2022-04-11 17:46:20 -04:00
/// <summary>Vendor-unique identifier.</summary>
public Guid VendorId = > GetInformation < Guid > ( GET_VIRTUAL_DISK_INFO_VERSION . GET_VIRTUAL_DISK_INFO_VIRTUAL_STORAGE_TYPE , 4 ) ;
2017-11-27 13:11:20 -05:00
2022-04-11 17:46:20 -04:00
/// <summary>The physical sector size of the virtual disk.</summary>
public uint VhdPhysicalSectorSize = > GetInformation < uint > ( GET_VIRTUAL_DISK_INFO_VERSION . GET_VIRTUAL_DISK_INFO_VHD_PHYSICAL_SECTOR_SIZE ) ;
2017-11-27 13:11:20 -05:00
2022-04-11 17:46:20 -04:00
/// <summary>
/// The identifier that is uniquely created when a user first creates the virtual disk to attempt to uniquely identify that virtual disk.
/// </summary>
public Guid VirtualDiskId
{
get = > GetInformation < Guid > ( GET_VIRTUAL_DISK_INFO_VERSION . GET_VIRTUAL_DISK_INFO_VIRTUAL_DISK_ID ) ;
set
2017-11-27 13:11:20 -05:00
{
2022-04-11 17:46:20 -04:00
SET_VIRTUAL_DISK_INFO si = new ( SET_VIRTUAL_DISK_INFO_VERSION . SET_VIRTUAL_DISK_INFO_VIRTUAL_DISK_ID ) { VirtualDiskId = value } ;
SetVirtualDiskInformation ( Handle , si ) . ThrowIfFailed ( ) ;
2017-11-27 13:11:20 -05:00
}
2022-04-11 17:46:20 -04:00
}
2017-11-27 13:11:20 -05:00
2022-04-11 17:46:20 -04:00
/// <summary>Virtual size of the VHD, in bytes.</summary>
public ulong VirtualSize = > GetInformation < ulong > ( GET_VIRTUAL_DISK_INFO_VERSION . GET_VIRTUAL_DISK_INFO_SIZE ) ;
2022-05-03 18:49:12 -04:00
/// <summary>Gets the volume GUID paths for an attached virtual disk.</summary>
2023-09-06 11:14:25 -04:00
public string [ ] ? VolumeGuidPaths
2022-05-03 18:49:12 -04:00
{
get
{
2023-09-06 11:14:25 -04:00
if ( ! Attached | | PhysicalPath is null )
2022-05-03 18:49:12 -04:00
return null ;
var diskNo = GetDiskNumberFromDevicePath ( PhysicalPath ) ;
return diskNo . HasValue ? GetVolumeGuidsFromDiskNumber ( diskNo . Value ) . ToArray ( ) : null ;
}
}
/// <summary>Gets the volume mount points for an attached virtual disk.</summary>
2023-09-06 11:14:25 -04:00
public string [ ] ? VolumeMountPoints
2022-05-03 18:49:12 -04:00
{
get
{
2022-05-06 17:25:03 -04:00
var volPath = VolumeGuidPaths ? . FirstOrDefault ( ) ;
2022-05-03 18:49:12 -04:00
return volPath is null ? null : GetVolumeMountPoints ( volPath ) ;
}
}
2022-04-11 17:46:20 -04:00
/// <summary>Gets the safe handle for the current virtual disk.</summary>
private SafeVIRTUAL_DISK_HANDLE Handle { get ; set ; }
/// <summary>Creates a virtual hard disk (VHD) image file.</summary>
/// <param name="path">A valid file path that represents the path to the new virtual disk image file.</param>
/// <param name="param">A reference to a valid CREATE_VIRTUAL_DISK_PARAMETERS structure that contains creation parameter data.</param>
/// <param name="flags">Creation flags, which must be a valid combination of the CREATE_VIRTUAL_DISK_FLAG enumeration.</param>
/// <param name="mask">The VIRTUAL_DISK_ACCESS_MASK value to use when opening the newly created virtual disk file.</param>
/// <param name="securityDescriptor">
/// An optional pointer to a SECURITY_DESCRIPTOR to apply to the virtual disk image file. If this parameter is IntPtr.Zero, the parent
/// directory's security descriptor will be used.
/// </param>
/// <returns>If successful, returns a valid <see cref="VirtualDisk"/> instance for the newly created virtual disk.</returns>
public static VirtualDisk Create ( string path , in CREATE_VIRTUAL_DISK_PARAMETERS param , CREATE_VIRTUAL_DISK_FLAG flags = CREATE_VIRTUAL_DISK_FLAG . CREATE_VIRTUAL_DISK_FLAG_NONE , VIRTUAL_DISK_ACCESS_MASK mask = VIRTUAL_DISK_ACCESS_MASK . VIRTUAL_DISK_ACCESS_NONE , PSECURITY_DESCRIPTOR securityDescriptor = default )
{
if ( string . IsNullOrEmpty ( path ) )
2017-11-27 13:11:20 -05:00
{
2022-04-11 17:46:20 -04:00
throw new ArgumentNullException ( nameof ( path ) ) ;
}
2017-11-27 13:11:20 -05:00
2022-04-11 17:46:20 -04:00
VIRTUAL_STORAGE_TYPE stType = new ( ) ;
CreateVirtualDisk ( stType , path , mask , securityDescriptor , flags , 0 , param , IntPtr . Zero , out SafeVIRTUAL_DISK_HANDLE handle ) . ThrowIfFailed ( ) ;
return new VirtualDisk ( handle , ( OPEN_VIRTUAL_DISK_VERSION ) param . Version , path ) ;
}
2022-03-11 20:07:36 -05:00
2022-04-11 17:46:20 -04:00
/// <summary>Creates a virtual hard disk (VHD) image file, either using default parameters or using an existing VHD or physical disk.</summary>
/// <param name="path">A valid file path that represents the path to the new virtual disk image file.</param>
/// <param name="size">The maximum virtual size, in bytes, of the virtual disk object. Must be a multiple of 512.</param>
/// <param name="dynamic">
/// <c>true</c> to grow the disk dynamically as content is added; <c>false</c> to pre-allocate all physical space necessary for the size
/// of the virtual disk.
/// </param>
/// <param name="access">
/// An optional FileSecurity instance to apply to the attached virtual disk. If this parameter is <c>null</c>, the security descriptor of
/// the virtual disk image file is used. Ensure that the security descriptor that AttachVirtualDisk applies to the attached virtual disk
/// grants the write attributes permission for the user, or that the security descriptor of the virtual disk image file grants the write
/// attributes permission for the user if you specify <c>null</c> for this parameter. If the security descriptor does not grant write
/// attributes permission for a user, Shell displays the following error when the user accesses the attached virtual disk: The Recycle
/// Bin is corrupted. Do you want to empty the Recycle Bin for this drive?
/// </param>
/// <returns>If successful, returns a valid <see cref="VirtualDisk"/> instance for the newly created virtual disk.</returns>
2023-09-06 11:14:25 -04:00
public static VirtualDisk Create ( string path , ulong size , bool dynamic = true , FileSecurity ? access = null ) = > Create ( path , size , dynamic , 0 , 0 , access ) ;
2022-04-11 17:46:20 -04:00
/// <summary>Creates a virtual hard disk (VHD) image file, either using default parameters or using an existing VHD or physical disk.</summary>
/// <param name="path">A valid file path that represents the path to the new virtual disk image file.</param>
/// <param name="size">The maximum virtual size, in bytes, of the virtual disk object. Must be a multiple of 512.</param>
/// <param name="dynamic">
/// <c>true</c> to grow the disk dynamically as content is added; <c>false</c> to pre-allocate all physical space necessary for the size
/// of the virtual disk.
/// </param>
/// <param name="blockSize">
/// Internal size of the virtual disk object blocks, in bytes. For VHDX this must be a multiple of 1 MB between 1 and 256 MB. For VHD 1
/// this must be set to one of the following values: 0 (default), 0x80000 (512K), or 0x200000 (2MB)
/// </param>
/// <param name="logicalSectorSize">
/// Internal size of the virtual disk object sectors. For VHDX must be set to 512 (0x200) or 4096 (0x1000). For VHD 1 must be set to 512.
/// </param>
/// <param name="access">
/// An optional FileSecurity instance to apply to the attached virtual disk. If this parameter is <c>null</c>, the security descriptor of
/// the virtual disk image file is used. Ensure that the security descriptor that AttachVirtualDisk applies to the attached virtual disk
/// grants the write attributes permission for the user, or that the security descriptor of the virtual disk image file grants the write
/// attributes permission for the user if you specify <c>null</c> for this parameter. If the security descriptor does not grant write
/// attributes permission for a user, Shell displays the following error when the user accesses the attached virtual disk: The Recycle
/// Bin is corrupted. Do you want to empty the Recycle Bin for this drive?
/// </param>
/// <returns>If successful, returns a valid <see cref="VirtualDisk"/> instance for the newly created virtual disk.</returns>
2023-09-06 11:14:25 -04:00
public static VirtualDisk Create ( string path , ulong size , bool dynamic , uint blockSize = 0 , uint logicalSectorSize = 0 , FileSecurity ? access = null )
2022-04-11 17:46:20 -04:00
{
if ( string . IsNullOrEmpty ( path ) )
{
throw new ArgumentNullException ( nameof ( path ) ) ;
2017-11-27 13:11:20 -05:00
}
2022-04-11 17:46:20 -04:00
VIRTUAL_DISK_ACCESS_MASK mask = IsPreWin8 ? VIRTUAL_DISK_ACCESS_MASK . VIRTUAL_DISK_ACCESS_CREATE : VIRTUAL_DISK_ACCESS_MASK . VIRTUAL_DISK_ACCESS_NONE ;
SafePSECURITY_DESCRIPTOR sd = FileSecToSd ( access ) ;
CREATE_VIRTUAL_DISK_PARAMETERS param = new ( size , IsPreWin8 ? 1 U : 2 U , blockSize , logicalSectorSize ) ;
CREATE_VIRTUAL_DISK_FLAG flags = dynamic ? CREATE_VIRTUAL_DISK_FLAG . CREATE_VIRTUAL_DISK_FLAG_NONE : CREATE_VIRTUAL_DISK_FLAG . CREATE_VIRTUAL_DISK_FLAG_FULL_PHYSICAL_ALLOCATION ;
return Create ( path , param , flags , mask , sd ) ;
}
/// <summary>Creates a virtual hard disk (VHD) image file.</summary>
/// <param name="path">A valid file path that represents the path to the new virtual disk image file.</param>
/// <param name="param">A reference to a valid CREATE_VIRTUAL_DISK_PARAMETERS structure that contains creation parameter data.</param>
/// <param name="flags">Creation flags, which must be a valid combination of the CREATE_VIRTUAL_DISK_FLAG enumeration.</param>
/// <param name="mask">The VIRTUAL_DISK_ACCESS_MASK value to use when opening the newly created virtual disk file.</param>
/// <param name="storageType">A VIRTUAL_STORAGE_TYPE structure that contains the desired disk type and vendor information.</param>
/// <param name="securityDescriptor">
/// An optional pointer to a SECURITY_DESCRIPTOR to apply to the virtual disk image file. If this parameter is IntPtr.Zero, the parent
/// directory's security descriptor will be used.
/// </param>
/// <param name="cancellationToken">
/// A cancellation token that can be used to cancel the operation. This value can be <c>default</c> to disable cancellation.
/// </param>
/// <param name="progress">
/// A class that implements <see cref="IProgress{T}"/> that can be used to report on progress. This value can be <c>null</c> to disable
/// progress reporting.
/// </param>
/// <returns>If successful, returns a valid <see cref="VirtualDisk"/> instance for the newly created virtual disk.</returns>
public static async Task < VirtualDisk > CreateAsync ( string path , CREATE_VIRTUAL_DISK_PARAMETERS param ,
CREATE_VIRTUAL_DISK_FLAG flags = CREATE_VIRTUAL_DISK_FLAG . CREATE_VIRTUAL_DISK_FLAG_NONE ,
VIRTUAL_DISK_ACCESS_MASK mask = VIRTUAL_DISK_ACCESS_MASK . VIRTUAL_DISK_ACCESS_NONE ,
VIRTUAL_STORAGE_TYPE storageType = default ,
PSECURITY_DESCRIPTOR securityDescriptor = default ,
2023-09-06 11:14:25 -04:00
CancellationToken cancellationToken = default , IProgress < int > ? progress = default )
2022-04-11 17:46:20 -04:00
{
if ( string . IsNullOrEmpty ( path ) )
2017-12-22 11:09:04 -05:00
{
2022-04-11 17:46:20 -04:00
throw new ArgumentNullException ( nameof ( path ) ) ;
}
2022-03-11 20:07:36 -05:00
2022-09-20 21:29:19 -04:00
return await Task . Run ( ( ) = >
{
NativeOverlapped vhdOverlap = new ( ) ;
CreateVirtualDisk ( storageType , path , mask , securityDescriptor , flags , 0 , param , vhdOverlap , out SafeVIRTUAL_DISK_HANDLE hVhd ) . ThrowUnless ( Win32Error . ERROR_IO_PENDING ) ;
2017-12-22 11:09:04 -05:00
2022-09-20 21:29:19 -04:00
if ( ! GetProgress ( hVhd , vhdOverlap , cancellationToken , progress ) )
throw new OperationCanceledException ( cancellationToken ) ;
2022-03-11 20:07:36 -05:00
2022-09-20 21:29:19 -04:00
return new VirtualDisk ( hVhd , ( OPEN_VIRTUAL_DISK_VERSION ) param . Version , path ) ;
} ) ;
2022-04-11 17:46:20 -04:00
}
2017-12-22 11:09:04 -05:00
2022-04-11 17:46:20 -04:00
/// <summary>Creates a virtual hard disk (VHD) image file, either using default parameters or using an existing VHD or physical disk.</summary>
/// <param name="path">A valid string that represents the path to the new virtual disk image file.</param>
/// <param name="parentPath"></param>
/// <param name="access">
/// An optional pointer to a FileSecurity instance to apply to the attached virtual disk. If this parameter is NULL, the security
/// descriptor of the virtual disk image file is used. Ensure that the security descriptor that AttachVirtualDisk applies to the attached
/// virtual disk grants the write attributes permission for the user, or that the security descriptor of the virtual disk image file
/// grants the write attributes permission for the user if you specify NULL for this parameter.If the security descriptor does not grant
/// write attributes permission for a user, Shell displays the following error when the user accesses the attached virtual disk: The
/// Recycle Bin is corrupted.Do you want to empty the Recycle Bin for this drive?
/// </param>
/// <returns></returns>
2023-09-06 11:14:25 -04:00
public static VirtualDisk CreateDifferencing ( string path , string parentPath , FileSecurity ? access = null )
2022-04-11 17:46:20 -04:00
{
//if (access == null) access = GetFileSecurity();
if ( string . IsNullOrEmpty ( path ) )
2022-04-09 20:40:26 -04:00
{
2022-04-11 17:46:20 -04:00
throw new ArgumentNullException ( nameof ( path ) ) ;
}
2022-04-09 20:40:26 -04:00
2022-04-11 17:46:20 -04:00
if ( string . IsNullOrEmpty ( parentPath ) )
{
throw new ArgumentNullException ( nameof ( parentPath ) ) ;
}
2022-04-09 20:40:26 -04:00
2022-04-11 17:46:20 -04:00
// If this is V2 (>=Win8), then let the file extension determine type, otherwise, it has to be a VHD
VIRTUAL_DISK_ACCESS_MASK mask = IsPreWin8 ? VIRTUAL_DISK_ACCESS_MASK . VIRTUAL_DISK_ACCESS_CREATE : VIRTUAL_DISK_ACCESS_MASK . VIRTUAL_DISK_ACCESS_NONE ;
SafePSECURITY_DESCRIPTOR sd = FileSecToSd ( access ) ;
using SafeCoTaskMemString pp = new ( parentPath ) ;
CREATE_VIRTUAL_DISK_PARAMETERS param = new ( pp , default ) ;
return Create ( path , param , CREATE_VIRTUAL_DISK_FLAG . CREATE_VIRTUAL_DISK_FLAG_NONE , mask , sd ) ;
}
2022-04-09 20:40:26 -04:00
2022-04-11 17:46:20 -04:00
/// <summary>Creates a virtual hard disk (VHD) image file, either using default parameters or using an existing VHD or physical disk.</summary>
/// <param name="path">A valid file path that represents the path to the new virtual disk image file.</param>
/// <param name="sourcePath">
/// A fully qualified path to pre-populate the new virtual disk object with block data from an existing disk. This path may refer to a
/// virtual disk or a physical disk.
/// </param>
/// <returns>If successful, returns a valid <see cref="VirtualDisk"/> instance for the newly created virtual disk.</returns>
public static VirtualDisk CreateFromSource ( string path , string sourcePath )
{
if ( string . IsNullOrEmpty ( path ) )
{
throw new ArgumentNullException ( nameof ( path ) ) ;
}
2022-04-09 20:40:26 -04:00
2022-04-11 17:46:20 -04:00
if ( string . IsNullOrEmpty ( sourcePath ) )
{
throw new ArgumentNullException ( nameof ( sourcePath ) ) ;
2022-04-09 20:40:26 -04:00
}
2022-04-11 17:46:20 -04:00
VIRTUAL_DISK_ACCESS_MASK mask = IsPreWin8 ? VIRTUAL_DISK_ACCESS_MASK . VIRTUAL_DISK_ACCESS_CREATE : VIRTUAL_DISK_ACCESS_MASK . VIRTUAL_DISK_ACCESS_NONE ;
using SafeCoTaskMemString sp = new ( sourcePath ) ;
CREATE_VIRTUAL_DISK_PARAMETERS param = new ( default , sp ) ;
return Create ( path , param , CREATE_VIRTUAL_DISK_FLAG . CREATE_VIRTUAL_DISK_FLAG_NONE , mask ) ;
}
/// <summary>Creates a virtual hard disk (VHD) image file, either using default parameters or using an existing VHD or physical disk.</summary>
/// <param name="path">A valid file path that represents the path to the new virtual disk image file.</param>
/// <param name="sourcePath">
/// A fully qualified path to pre-populate the new virtual disk object with block data from an existing disk. This path may refer to a
/// virtual disk or a physical disk.
/// </param>
/// <param name="cancellationToken">
/// A cancellation token that can be used to cancel the operation. This value can be <c>default</c> to disable cancellation.
/// </param>
/// <param name="progress">
/// A class that implements <see cref="IProgress{T}"/> that can be used to report on progress. This value can be <c>null</c> to disable
/// progress reporting.
/// </param>
/// <returns>If successful, returns a valid <see cref="VirtualDisk"/> instance for the newly created virtual disk.</returns>
2023-09-06 11:14:25 -04:00
public static async Task < VirtualDisk > CreateFromSourceAsync ( string path , string sourcePath , CancellationToken cancellationToken = default , IProgress < int > ? progress = default )
2022-04-11 17:46:20 -04:00
{
if ( string . IsNullOrEmpty ( path ) )
2017-11-27 13:11:20 -05:00
{
2022-04-11 17:46:20 -04:00
throw new ArgumentNullException ( nameof ( path ) ) ;
2017-11-27 13:11:20 -05:00
}
2022-04-11 17:46:20 -04:00
if ( string . IsNullOrEmpty ( sourcePath ) )
2017-11-27 13:11:20 -05:00
{
2022-04-11 17:46:20 -04:00
throw new ArgumentNullException ( nameof ( sourcePath ) ) ;
}
2022-03-11 20:07:36 -05:00
2022-04-11 17:46:20 -04:00
VIRTUAL_STORAGE_TYPE stType = new ( ) ;
VIRTUAL_DISK_ACCESS_MASK mask = IsPreWin8 ? VIRTUAL_DISK_ACCESS_MASK . VIRTUAL_DISK_ACCESS_CREATE : VIRTUAL_DISK_ACCESS_MASK . VIRTUAL_DISK_ACCESS_NONE ;
using SafeCoTaskMemString sp = new ( sourcePath ) ;
CREATE_VIRTUAL_DISK_PARAMETERS param = new ( default , sp ) ;
CREATE_VIRTUAL_DISK_FLAG flags = CREATE_VIRTUAL_DISK_FLAG . CREATE_VIRTUAL_DISK_FLAG_NONE ;
return await CreateAsync ( path , param , flags , mask , stType , default , cancellationToken , progress ) ;
}
2022-03-11 20:07:36 -05:00
2022-04-11 17:46:20 -04:00
/// <summary>
/// Detach a virtual disk that was previously attached with the ATTACH_VIRTUAL_DISK_FLAG_PERMANENT_LIFETIME flag or calling <see
/// cref="Attach(bool, bool, bool, FileSecurity)"/> and setting autoDetach to <c>false</c>.
/// </summary>
/// <param name="path">A valid path to the virtual disk image to detach.</param>
public static void Detach ( string path )
{
try
{
using VirtualDisk vd = Open ( path , OPEN_VIRTUAL_DISK_FLAG . OPEN_VIRTUAL_DISK_FLAG_NONE , null , VIRTUAL_DISK_ACCESS_MASK . VIRTUAL_DISK_ACCESS_DETACH ) ;
vd . Detach ( ) ;
2017-11-27 13:11:20 -05:00
}
2022-04-11 17:46:20 -04:00
catch { }
}
2017-11-27 13:11:20 -05:00
2022-04-11 17:46:20 -04:00
/// <summary>Gets the list of all the loopback mounted virtual disks.</summary>
/// <returns>An enumeration of all the loopback mounted virtual disks physical paths.</returns>
public static IEnumerable < string > GetAllAttachedVirtualDiskPaths ( )
{
uint sz = 0 ;
SafeCoTaskMemHandle sb = new ( 0 ) ;
Win32Error err ;
do
2022-03-11 20:07:36 -05:00
{
2022-04-11 17:46:20 -04:00
err = GetAllAttachedVirtualDiskPhysicalPaths ( ref sz , sb ) ;
if ( err . Succeeded )
2022-03-11 20:07:36 -05:00
{
2022-04-11 17:46:20 -04:00
break ;
2022-03-11 20:07:36 -05:00
}
2022-04-11 17:46:20 -04:00
if ( err ! = Win32Error . ERROR_INSUFFICIENT_BUFFER )
2022-03-11 20:07:36 -05:00
{
2022-04-11 17:46:20 -04:00
err . ThrowIfFailed ( ) ;
2022-03-11 20:07:36 -05:00
}
2022-04-11 17:46:20 -04:00
sb . Size = ( int ) sz ;
} while ( err = = Win32Error . ERROR_INSUFFICIENT_BUFFER ) ;
return sb . Size < = 1 ? new string [ 0 ] : sb . ToStringEnum ( CharSet . Unicode ) ;
}
/// <summary>
/// Returns the relationships between virtual hard disks (VHDs) or CD or DVD image file (ISO) or the volumes contained within those disks
/// and their parent disk or volume.
/// </summary>
/// <param name="handle">
/// <para>
/// A handle to a volume or root directory if the <c>Flags</c> parameter does not specify the
/// <c>GET_STORAGE_DEPENDENCY_FLAG_DISK_HANDLE</c> flag. For information on how to open a volume or root directory, see the CreateFile function.
/// </para>
/// <para>
/// If the <c>Flags</c> parameter specifies the <c>GET_STORAGE_DEPENDENCY_FLAG_DISK_HANDLE</c> flag, this handle should be a handle to a disk.
/// </para>
/// </param>
/// <param name="flags">A valid combination of GET_STORAGE_DEPENDENCY_FLAG values.</param>
/// <returns>An array of <see cref="STORAGE_DEPENDENCY_INFO_TYPE_2"/> structures with the requested information.</returns>
/// <remarks>CD and DVD image files (ISO) are not supported before Windows 8 and Windows Server 2012.</remarks>
public static STORAGE_DEPENDENCY_INFO_TYPE_2 [ ] GetStorageDependencyInformation ( IntPtr handle , GET_STORAGE_DEPENDENCY_FLAG flags )
{
using var mem = SafeCoTaskMemHandle . CreateFromStructure ( new STORAGE_DEPENDENCY_INFO { Version = STORAGE_DEPENDENCY_INFO_VERSION . STORAGE_DEPENDENCY_INFO_VERSION_2 } ) ;
Win32Error err = VirtDisk . GetStorageDependencyInformation ( handle , flags , mem . Size , mem , out var sz ) ;
if ( err = = Win32Error . ERROR_INSUFFICIENT_BUFFER )
{
mem . Size = sz ;
err = VirtDisk . GetStorageDependencyInformation ( handle , flags , mem . Size , mem , out sz ) ;
2017-11-27 13:11:20 -05:00
}
2022-04-11 17:46:20 -04:00
err . ThrowIfFailed ( ) ;
// Get array at offset 8 from count at offset 4
return mem . ToArray < STORAGE_DEPENDENCY_INFO_TYPE_2 > ( mem . ToStructure < int > ( sizeof ( int ) ) , sizeof ( uint ) * 2 ) ;
}
2017-11-27 13:11:20 -05:00
2022-04-11 17:46:20 -04:00
/// <summary>Determines whether the specified virtual disk indicated by <paramref name="path"/> is currently attached.</summary>
/// <param name="path">The path to the virtual disk image file.</param>
/// <returns><see langword="true"/> if the specified path is attached; otherwise, <see langword="false"/>.</returns>
public static bool IsAttached ( string path ) = > GetAllAttachedVirtualDiskPaths ( ) . Any ( s = > s . Equals ( path , StringComparison . InvariantCultureIgnoreCase ) ) ;
/// <summary>Creates an instance of a Virtual Disk from a file.</summary>
/// <param name="path">A valid path to the virtual disk image to open.</param>
/// <param name="flags">A valid combination of values of the OPEN_VIRTUAL_DISK_FLAG enumeration.</param>
/// <param name="param">A valid OPEN_VIRTUAL_DISK_PARAMETERS structure.</param>
/// <param name="mask">A valid VIRTUAL_DISK_ACCESS_MASK value.</param>
2023-09-06 11:14:25 -04:00
public static VirtualDisk Open ( string path , OPEN_VIRTUAL_DISK_FLAG flags = OPEN_VIRTUAL_DISK_FLAG . OPEN_VIRTUAL_DISK_FLAG_NONE , OPEN_VIRTUAL_DISK_PARAMETERS ? param = null , VIRTUAL_DISK_ACCESS_MASK mask = VIRTUAL_DISK_ACCESS_MASK . VIRTUAL_DISK_ACCESS_NONE )
2022-04-11 17:46:20 -04:00
{
if ( string . IsNullOrEmpty ( path ) )
2017-11-27 13:11:20 -05:00
{
2022-04-11 17:46:20 -04:00
throw new ArgumentNullException ( nameof ( path ) ) ;
}
2022-03-11 20:07:36 -05:00
2022-04-11 17:46:20 -04:00
Debug . WriteLine ( $"OpenVD: mask={mask}; flags={flags}; param={param}" ) ;
OpenVirtualDisk ( new VIRTUAL_STORAGE_TYPE ( ) , path , mask , flags , param , out SafeVIRTUAL_DISK_HANDLE hVhd ) . ThrowIfFailed ( ) ;
return new VirtualDisk ( hVhd , param ? . Version ? ? ( IsPreWin8 ? OPEN_VIRTUAL_DISK_VERSION . OPEN_VIRTUAL_DISK_VERSION_1 : OPEN_VIRTUAL_DISK_VERSION . OPEN_VIRTUAL_DISK_VERSION_2 ) , path ) ;
}
2022-03-11 20:07:36 -05:00
2022-04-11 17:46:20 -04:00
/// <summary>Creates an instance of a Virtual Disk from a file.</summary>
/// <param name="path">A valid path to the virtual disk image to open.</param>
/// <param name="readOnly">If TRUE, indicates the file backing store is to be opened as read-only.</param>
/// <param name="getInfoOnly">If TRUE, indicates the handle is only to be used to get information on the virtual disk.</param>
/// <param name="noParents">
/// Open the VHD file (backing store) without opening any differencing-chain parents. Used to correct broken parent links. This flag is
/// not supported for ISO virtual disks.
/// </param>
public static VirtualDisk Open ( string path , bool readOnly , bool getInfoOnly = false , bool noParents = false )
{
if ( string . IsNullOrEmpty ( path ) )
throw new ArgumentNullException ( nameof ( path ) ) ;
2022-03-11 20:07:36 -05:00
2022-04-11 17:46:20 -04:00
OPEN_VIRTUAL_DISK_FLAG flags = OPEN_VIRTUAL_DISK_FLAG . OPEN_VIRTUAL_DISK_FLAG_NONE ;
if ( noParents )
flags | = OPEN_VIRTUAL_DISK_FLAG . OPEN_VIRTUAL_DISK_FLAG_NO_PARENTS ;
2022-03-11 20:07:36 -05:00
2022-04-11 17:46:20 -04:00
var isIso = Path . GetExtension ( path ) . Equals ( ".iso" , StringComparison . InvariantCultureIgnoreCase ) ;
if ( isIso & & ( ! readOnly | | noParents ) )
throw new NotSupportedException ( ) ;
OPEN_VIRTUAL_DISK_PARAMETERS param ;
VIRTUAL_DISK_ACCESS_MASK mask = VIRTUAL_DISK_ACCESS_MASK . VIRTUAL_DISK_ACCESS_NONE ;
if ( isIso | | IsPreWin8 )
{
param = new OPEN_VIRTUAL_DISK_PARAMETERS ( 0 ) ; // make v1 instance
if ( readOnly )
mask | = VIRTUAL_DISK_ACCESS_MASK . VIRTUAL_DISK_ACCESS_READ ;
2022-03-11 20:07:36 -05:00
2022-04-11 17:46:20 -04:00
if ( getInfoOnly )
mask | = VIRTUAL_DISK_ACCESS_MASK . VIRTUAL_DISK_ACCESS_GET_INFO ;
}
else
{
param = new OPEN_VIRTUAL_DISK_PARAMETERS ( readOnly , getInfoOnly ) ;
2017-11-27 13:11:20 -05:00
}
2022-04-11 17:46:20 -04:00
return Open ( path , flags , param , mask ) ;
}
/// <summary>Attaches a parent to a virtual disk opened with the OPEN_VIRTUAL_DISK_FLAG_CUSTOM_DIFF_CHAIN flag.</summary>
/// <param name="parentPath">A valid path to the virtual hard disk image to add as a parent.</param>
/// <remarks>
/// This adds the specified parent virtual hard disk to the head of the differencing chain of the specified virtual hard disk. If the
/// differencing chain extends beyond the parent, this function can be called repeatedly to add additional parents to the differencing chain.
/// </remarks>
public void AddParent ( string parentPath ) = > AddVirtualDiskParent ( Handle , parentPath ) . ThrowIfFailed ( ) ;
/// <summary>Applies a snapshot of the current virtual disk for VHD Set files.</summary>
/// <param name="id">The ID of the new snapshot to be applied to the VHD set.</param>
/// <param name="leafId">
/// Indicates whether the current default leaf data should be retained as part of the apply operation. When <see cref="Guid.Empty"/> is
/// specified, the apply operation will discard the current default leaf data. When a non-zero GUID is specified, the apply operation
/// will convert the default leaf data into a writeable snapshot with the specified ID.
/// </param>
/// <param name="writeable"><see langword="true"/> to indicate that the snapshot to be applied was created as a writable snapshot type.</param>
public void ApplySnapshot ( Guid id , Guid leafId = default , bool writeable = false )
{
APPLY_SNAPSHOT_VHDSET_PARAMETERS param = new ( )
2017-11-27 13:11:20 -05:00
{
2022-04-11 17:46:20 -04:00
Version = APPLY_SNAPSHOT_VHDSET_VERSION . APPLY_SNAPSHOT_VHDSET_VERSION_1 ,
Version1 = new ( ) { SnapshotId = id , LeafSnapshotId = leafId }
} ;
ApplySnapshotVhdSet ( Handle , param , writeable ? 0 : APPLY_SNAPSHOT_VHDSET_FLAG . APPLY_SNAPSHOT_VHDSET_FLAG_WRITEABLE ) . ThrowIfFailed ( ) ;
}
2022-03-11 20:07:36 -05:00
2022-04-11 17:46:20 -04:00
/// <summary>
/// Attaches a virtual hard disk (VHD) or CD or DVD image file (ISO) by locating an appropriate VHD provider to accomplish the attachment.
/// </summary>
/// <param name="flags">A valid combination of values of the ATTACH_VIRTUAL_DISK_FLAG enumeration.</param>
/// <param name="param">A reference to a valid ATTACH_VIRTUAL_DISK_PARAMETERS structure that contains attachment parameter data.</param>
/// <param name="securityDescriptor">
/// An optional pointer to a SECURITY_DESCRIPTOR to apply to the attached virtual disk. If this parameter is NULL, the security
/// descriptor of the virtual disk image file is used.
/// <para>
/// Ensure that the security descriptor that AttachVirtualDisk applies to the attached virtual disk grants the write attributes
/// permission for the user, or that the security descriptor of the virtual disk image file grants the write attributes permission for
/// the user if you specify NULL for this parameter. If the security descriptor does not grant write attributes permission for a user,
/// Shell displays the following error when the user accesses the attached virtual disk: The Recycle Bin is corrupted. Do you want to
/// empty the Recycle Bin for this drive?
/// </para>
/// </param>
public void Attach ( ATTACH_VIRTUAL_DISK_FLAG flags , ATTACH_VIRTUAL_DISK_PARAMETERS ? param = null , PSECURITY_DESCRIPTOR securityDescriptor = default )
{
if ( ! securityDescriptor . IsNull & & ! securityDescriptor . IsValidSecurityDescriptor ( ) )
{
throw new ArgumentException ( "Invalid security descriptor." ) ;
2017-11-27 13:11:20 -05:00
}
2022-04-11 17:46:20 -04:00
if ( param . HasValue )
AttachVirtualDisk ( Handle , securityDescriptor , flags , 0 , param . Value , IntPtr . Zero ) . ThrowIfFailed ( ) ;
else
AttachVirtualDisk ( Handle , securityDescriptor , flags , 0 , IntPtr . Zero , IntPtr . Zero ) . ThrowIfFailed ( ) ;
}
2022-03-11 20:07:36 -05:00
2022-04-11 17:46:20 -04:00
/// <summary>
/// Attaches a virtual hard disk (VHD) or CD or DVD image file (ISO) by locating an appropriate VHD provider to accomplish the attachment.
/// </summary>
/// <param name="readOnly">Attach the virtual disk as read-only.</param>
/// <param name="autoDetach">
/// If <c>false</c>, decouple the virtual disk lifetime from that of the VirtualDisk. The virtual disk will be attached until the Detach
/// function is called, even if all open instances of the virtual disk are disposed.
/// </param>
/// <param name="noDriveLetter">No drive letters are assigned to the disk's volumes.</param>
/// <param name="access">
/// An optional pointer to a FileSecurity instance to apply to the attached virtual disk. If this parameter is <see langword="null"/>,
/// the security descriptor of the virtual disk image file is used. Ensure that the security descriptor that AttachVirtualDisk applies to
/// the attached virtual disk grants the write attributes permission for the user, or that the security descriptor of the virtual disk
/// image file grants the write attributes permission for the user if you specify <see langword="null"/> for this parameter. If the
/// security descriptor does not grant write attributes permission for a user, Shell displays the following error when the user accesses
/// the attached virtual disk: The Recycle Bin is corrupted. Do you want to empty the Recycle Bin for this drive?
/// </param>
2023-09-06 11:14:25 -04:00
public void Attach ( bool readOnly = false , bool autoDetach = true , bool noDriveLetter = false , FileSecurity ? access = null ) = >
2022-05-06 17:25:03 -04:00
Attach ( noDriveLetter ? null : new [ ] { "*" } , readOnly , autoDetach , access ) ;
2022-05-03 18:49:12 -04:00
/// <summary>
/// Attaches a virtual hard disk (VHD) or CD or DVD image file (ISO) by locating an appropriate VHD provider to accomplish the attachment.
/// </summary>
/// <param name="mountPoints">
/// The user-mode paths to be associated with the volume. These may be a drive letter (for example, "X:\") or a directory on another
/// volume (for example, "Y:\MountX\"). The string must end with a trailing backslash ('\'). Use "*" as the first and only entry to have
/// the system assign a drive letter or <see langword="null"/> to leave the drive letter unassigned.
/// </param>
/// <param name="readOnly">Attach the virtual disk as read-only.</param>
/// <param name="autoDetach">
/// If <c>false</c>, decouple the virtual disk lifetime from that of the VirtualDisk. The virtual disk will be attached until the Detach
/// function is called, even if all open instances of the virtual disk are disposed.
/// </param>
/// <param name="access">
/// An optional pointer to a FileSecurity instance to apply to the attached virtual disk. If this parameter is <see langword="null"/>,
/// the security descriptor of the virtual disk image file is used. Ensure that the security descriptor that AttachVirtualDisk applies to
/// the attached virtual disk grants the write attributes permission for the user, or that the security descriptor of the virtual disk
/// image file grants the write attributes permission for the user if you specify <see langword="null"/> for this parameter. If the
/// security descriptor does not grant write attributes permission for a user, Shell displays the following error when the user accesses
/// the attached virtual disk: The Recycle Bin is corrupted. Do you want to empty the Recycle Bin for this drive?
/// </param>
2023-09-06 11:14:25 -04:00
public void Attach ( string [ ] ? mountPoints , bool readOnly = false , bool autoDetach = true , FileSecurity ? access = null )
2022-04-11 17:46:20 -04:00
{
2022-05-06 17:25:03 -04:00
if ( mountPoints is not null & & mountPoints . Length = = 0 )
throw new ArgumentException ( "Mount points list cannot be empty." , nameof ( mountPoints ) ) ;
if ( mountPoints is not null & & mountPoints [ 0 ] ! = "*" & & mountPoints . Any ( p = > p = = "*" ) )
throw new ArgumentException ( "Mount points may only contain the '*' value as the first and only value." , nameof ( mountPoints ) ) ;
2022-05-03 18:49:12 -04:00
2022-05-04 17:49:33 -04:00
ATTACH_VIRTUAL_DISK_FLAG flags = 0 ;
2022-05-06 17:25:03 -04:00
if ( mountPoints is null | | ( mountPoints . Length > 0 & & mountPoints [ 0 ] ! = "*" ) )
2022-05-04 17:49:33 -04:00
flags | = ATTACH_VIRTUAL_DISK_FLAG . ATTACH_VIRTUAL_DISK_FLAG_NO_DRIVE_LETTER ;
if ( readOnly )
flags | = ATTACH_VIRTUAL_DISK_FLAG . ATTACH_VIRTUAL_DISK_FLAG_READ_ONLY ;
2022-04-11 17:46:20 -04:00
if ( ! autoDetach )
flags | = ATTACH_VIRTUAL_DISK_FLAG . ATTACH_VIRTUAL_DISK_FLAG_PERMANENT_LIFETIME ;
if ( access is null )
flags | = ATTACH_VIRTUAL_DISK_FLAG . ATTACH_VIRTUAL_DISK_FLAG_NO_SECURITY_DESCRIPTOR ;
2022-03-11 20:07:36 -05:00
2022-04-11 17:46:20 -04:00
using SafePSECURITY_DESCRIPTOR sd = FileSecToSd ( access ) ;
2022-05-06 17:25:03 -04:00
Attach ( flags , ATTACH_VIRTUAL_DISK_PARAMETERS . Default , sd ) ;
if ( mountPoints is null | | mountPoints [ 0 ] is "*" )
return ;
2022-05-03 18:49:12 -04:00
2023-09-06 11:14:25 -04:00
var vgp = VolumeGuidPaths ? ? new string [ 0 ] ;
2022-05-06 17:25:03 -04:00
if ( mountPoints . Length > vgp . Length )
{
Detach ( ) ;
throw new ArgumentException ( "The number of mount points cannot be larger than the number of associated volumes." , nameof ( mountPoints ) ) ;
}
2022-05-03 18:49:12 -04:00
for ( var i = 0 ; i < mountPoints . Length ; i + + )
Win32Error . ThrowLastErrorIfFalse ( SetVolumeMountPoint ( mountPoints [ i ] , vgp [ i ] ) ) ;
2022-04-11 17:46:20 -04:00
}
2022-03-11 20:07:36 -05:00
2022-04-11 17:46:20 -04:00
/// <summary>
/// Attaches a virtual hard disk (VHD) or CD or DVD image file (ISO) by locating an appropriate VHD provider to accomplish the attachment.
/// </summary>
/// <param name="flags">A valid combination of values of the ATTACH_VIRTUAL_DISK_FLAG enumeration.</param>
/// <param name="param">A reference to a valid ATTACH_VIRTUAL_DISK_PARAMETERS structure that contains attachment parameter data.</param>
/// <param name="securityDescriptor">
/// An optional pointer to a SECURITY_DESCRIPTOR to apply to the attached virtual disk. If this parameter is NULL, the security
/// descriptor of the virtual disk image file is used.
/// <para>
/// Ensure that the security descriptor that AttachVirtualDisk applies to the attached virtual disk grants the write attributes
/// permission for the user, or that the security descriptor of the virtual disk image file grants the write attributes permission for
/// the user if you specify NULL for this parameter. If the security descriptor does not grant write attributes permission for a user,
/// Shell displays the following error when the user accesses the attached virtual disk: The Recycle Bin is corrupted. Do you want to
/// empty the Recycle Bin for this drive?
/// </para>
/// </param>
/// <param name="cancellationToken">
/// A cancellation token that can be used to cancel the operation. This value can be <c>default</c> to disable cancellation.
/// </param>
/// <param name="progress">
/// A class that implements <see cref="IProgress{T}"/> that can be used to report on progress. This value can be <c>null</c> to disable
/// progress reporting.
/// </param>
public async Task AttachAsync ( ATTACH_VIRTUAL_DISK_FLAG flags , ATTACH_VIRTUAL_DISK_PARAMETERS ? param = null , PSECURITY_DESCRIPTOR securityDescriptor = default ,
2023-09-06 11:14:25 -04:00
CancellationToken cancellationToken = default , IProgress < int > ? progress = default )
2022-04-11 17:46:20 -04:00
{
if ( ! securityDescriptor . IsNull & & ! securityDescriptor . IsValidSecurityDescriptor ( ) )
throw new ArgumentException ( "Invalid security descriptor." ) ;
2017-11-27 13:11:20 -05:00
2022-09-20 21:29:19 -04:00
await RunAsync ( Handle , ( in NativeOverlapped vhdOverlap ) = >
AttachVirtualDisk ( Handle , securityDescriptor , flags , 0 , param ? ? ATTACH_VIRTUAL_DISK_PARAMETERS . Default , vhdOverlap ) , cancellationToken , progress ) ;
2022-04-11 17:46:20 -04:00
}
2017-11-27 13:11:20 -05:00
2022-04-11 17:46:20 -04:00
/// <summary>
/// Attaches a virtual hard disk (VHD) or CD or DVD image file (ISO) by locating an appropriate VHD provider to accomplish the attachment.
/// </summary>
/// <param name="readOnly">Attach the virtual disk as read-only.</param>
/// <param name="autoDetach">
/// If <c>false</c>, decouple the virtual disk lifetime from that of the VirtualDisk. The virtual disk will be attached until the Detach
/// function is called, even if all open instances of the virtual disk are disposed.
/// </param>
/// <param name="noDriveLetter">No drive letters are assigned to the disk's volumes.</param>
/// <param name="access">
/// An optional pointer to a FileSecurity instance to apply to the attached virtual disk. If this parameter is <see langword="null"/>,
/// the security descriptor of the virtual disk image file is used. Ensure that the security descriptor that AttachVirtualDisk applies to
/// the attached virtual disk grants the write attributes permission for the user, or that the security descriptor of the virtual disk
/// image file grants the write attributes permission for the user if you specify <see langword="null"/> for this parameter. If the
/// security descriptor does not grant write attributes permission for a user, Shell displays the following error when the user accesses
/// the attached virtual disk: The Recycle Bin is corrupted. Do you want to empty the Recycle Bin for this drive?
/// </param>
/// <param name="cancellationToken">
/// A cancellation token that can be used to cancel the operation. This value can be <c>default</c> to disable cancellation.
/// </param>
/// <param name="progress">
/// A class that implements <see cref="IProgress{T}"/> that can be used to report on progress. This value can be <c>null</c> to disable
/// progress reporting.
/// </param>
2023-09-06 11:14:25 -04:00
public async Task AttachAsync ( bool readOnly = false , bool autoDetach = true , bool noDriveLetter = false , FileSecurity ? access = null ,
CancellationToken cancellationToken = default , IProgress < int > ? progress = default ) = >
2022-05-06 17:25:03 -04:00
await AttachAsync ( noDriveLetter ? null : new [ ] { "*" } , readOnly , autoDetach , access , cancellationToken , progress ) ;
2022-03-11 20:07:36 -05:00
2022-04-11 17:46:20 -04:00
/// <summary>
/// Attaches a virtual hard disk (VHD) or CD or DVD image file (ISO) by locating an appropriate VHD provider to accomplish the attachment.
/// </summary>
2022-05-06 17:25:03 -04:00
/// <param name="mountPoints">
2022-04-11 17:46:20 -04:00
/// The user-mode path to be associated with the volume. This may be a drive letter (for example, "X:\") or a directory on another volume
/// (for example, "Y:\MountX\"). The string must end with a trailing backslash ('\'). Use "*" to have the system assign a drive letter or
/// <see langword="null"/> to leave the drive letter unassigned.
/// </param>
/// <param name="readOnly">Attach the virtual disk as read-only.</param>
/// <param name="autoDetach">
/// If <c>false</c>, decouple the virtual disk lifetime from that of the VirtualDisk. The virtual disk will be attached until the Detach
/// function is called, even if all open instances of the virtual disk are disposed.
/// </param>
/// <param name="access">
/// An optional pointer to a FileSecurity instance to apply to the attached virtual disk. If this parameter is <see langword="null"/>,
/// the security descriptor of the virtual disk image file is used. Ensure that the security descriptor that AttachVirtualDisk applies to
/// the attached virtual disk grants the write attributes permission for the user, or that the security descriptor of the virtual disk
/// image file grants the write attributes permission for the user if you specify <see langword="null"/> for this parameter. If the
/// security descriptor does not grant write attributes permission for a user, Shell displays the following error when the user accesses
/// the attached virtual disk: The Recycle Bin is corrupted. Do you want to empty the Recycle Bin for this drive?
/// </param>
/// <param name="cancellationToken">
/// A cancellation token that can be used to cancel the operation. This value can be <c>default</c> to disable cancellation.
/// </param>
/// <param name="progress">
/// A class that implements <see cref="IProgress{T}"/> that can be used to report on progress. This value can be <c>null</c> to disable
/// progress reporting.
/// </param>
2023-09-06 11:14:25 -04:00
public async Task AttachAsync ( string [ ] ? mountPoints , bool readOnly = false , bool autoDetach = true , FileSecurity ? access = null ,
CancellationToken cancellationToken = default , IProgress < int > ? progress = default )
2022-04-11 17:46:20 -04:00
{
2022-05-06 17:25:03 -04:00
if ( mountPoints is not null & & mountPoints . Length = = 0 )
throw new ArgumentException ( "Mount points list cannot be empty." , nameof ( mountPoints ) ) ;
if ( mountPoints is not null & & mountPoints [ 0 ] ! = "*" & & mountPoints . Any ( p = > p = = "*" ) )
throw new ArgumentException ( "Mount points may only contain the '*' value as the first and only value." , nameof ( mountPoints ) ) ;
2017-11-27 13:11:20 -05:00
2022-05-06 17:25:03 -04:00
ATTACH_VIRTUAL_DISK_FLAG flags = 0 ;
if ( mountPoints is null | | ( mountPoints . Length > 0 & & mountPoints [ 0 ] ! = "*" ) )
2022-04-11 17:46:20 -04:00
flags | = ATTACH_VIRTUAL_DISK_FLAG . ATTACH_VIRTUAL_DISK_FLAG_NO_DRIVE_LETTER ;
2022-05-06 17:25:03 -04:00
if ( readOnly )
flags | = ATTACH_VIRTUAL_DISK_FLAG . ATTACH_VIRTUAL_DISK_FLAG_READ_ONLY ;
if ( ! autoDetach )
flags | = ATTACH_VIRTUAL_DISK_FLAG . ATTACH_VIRTUAL_DISK_FLAG_PERMANENT_LIFETIME ;
2022-04-11 17:46:20 -04:00
if ( access is null )
flags | = ATTACH_VIRTUAL_DISK_FLAG . ATTACH_VIRTUAL_DISK_FLAG_NO_SECURITY_DESCRIPTOR ;
2019-10-04 08:55:01 -04:00
2022-04-11 17:46:20 -04:00
using SafePSECURITY_DESCRIPTOR sd = FileSecToSd ( access ) ;
2022-05-06 17:25:03 -04:00
await AttachAsync ( flags , ATTACH_VIRTUAL_DISK_PARAMETERS . Default , sd , cancellationToken , progress ) ;
if ( mountPoints is null | | mountPoints [ 0 ] is "*" )
return ;
2023-09-06 11:14:25 -04:00
var vgp = VolumeGuidPaths ? ? new string [ 0 ] ;
2022-05-06 17:25:03 -04:00
if ( mountPoints . Length > vgp . Length )
{
Detach ( ) ;
throw new ArgumentException ( "The number of mount points cannot be larger than the number of associated volumes." , nameof ( mountPoints ) ) ;
}
for ( var i = 0 ; i < mountPoints . Length ; i + + )
Win32Error . ThrowLastErrorIfFalse ( SetVolumeMountPoint ( mountPoints [ i ] , vgp [ i ] ) ) ;
2022-04-11 17:46:20 -04:00
}
2017-11-27 13:11:20 -05:00
2022-04-11 17:46:20 -04:00
/// <summary>Breaks a previously initiated mirror operation and sets the mirror to be the active virtual disk.</summary>
public void BreakMirror ( ) = > BreakMirrorVirtualDisk ( Handle ) . ThrowIfFailed ( ) ;
/// <summary>Closes the instance of the virtual disk.</summary>
public void Close ( ) = > Dispose ( ) ;
/// <summary>Reduces the size of a virtual hard disk (VHD) backing store file.</summary>
public void Compact ( ) = > CompactVirtualDisk ( Handle , COMPACT_VIRTUAL_DISK_FLAG . COMPACT_VIRTUAL_DISK_FLAG_NONE , COMPACT_VIRTUAL_DISK_PARAMETERS . Default , IntPtr . Zero ) . ThrowIfFailed ( ) ;
/// <summary>Reduces the size of a virtual hard disk (VHD) backing store file.</summary>
/// <param name="cancellationToken">
/// A cancellation token that can be used to cancel the operation. This value can be <c>default</c> to disable cancellation.
/// </param>
/// <param name="progress">
/// A class that implements <see cref="IProgress{T}"/> that can be used to report on progress. This value can be <c>null</c> to disable
/// progress reporting.
/// </param>
/// <returns><c>true</c> if operation completed without error or cancellation; <c>false</c> otherwise.</returns>
2023-09-06 11:14:25 -04:00
public async Task < bool > CompactAsync ( CancellationToken cancellationToken = default , IProgress < int > ? progress = default ) = >
2022-09-20 21:29:19 -04:00
await RunAsync ( Handle , ( in NativeOverlapped vhdOverlap ) = >
CompactVirtualDisk ( Handle , COMPACT_VIRTUAL_DISK_FLAG . COMPACT_VIRTUAL_DISK_FLAG_NONE , COMPACT_VIRTUAL_DISK_PARAMETERS . Default , vhdOverlap ) , cancellationToken , progress ) ;
2022-04-11 17:46:20 -04:00
/// <summary>Returns the value of the handle field.</summary>
/// <returns>An IntPtr representing the value of the handle field.</returns>
public IntPtr DangerousGetHandle ( ) = > ( ( IHandle ) Handle ) . DangerousGetHandle ( ) ;
/// <summary>Deletes a snapshot from a VHD Set file.</summary>
/// <param name="id">The Snapshot Id in GUID format indicating which snapshot is to be deleted from the VHD Set.</param>
/// <param name="persistReferencePoint">
/// If set to <see langword="true"/>, a reference point should be persisted in the VHD Set after the snapshot is deleted..
/// </param>
/// <returns></returns>
public void DeleteSnapshot ( Guid id , bool persistReferencePoint = false )
{
DELETE_SNAPSHOT_VHDSET_PARAMETERS param = new ( )
2017-12-22 11:09:04 -05:00
{
2022-04-11 17:46:20 -04:00
Version = DELETE_SNAPSHOT_VHDSET_VERSION . DELETE_SNAPSHOT_VHDSET_VERSION_1 ,
Version1 = new ( ) { SnapshotId = id }
} ;
DeleteSnapshotVhdSet ( Handle , param , persistReferencePoint ? 0 : DELETE_SNAPSHOT_VHDSET_FLAG . DELETE_SNAPSHOT_VHDSET_FLAG_PERSIST_RCT ) . ThrowIfFailed ( ) ;
}
2017-12-22 11:09:04 -05:00
2022-04-11 17:46:20 -04:00
/// <summary>
/// Detaches a virtual hard disk (VHD) or CD or DVD image file (ISO) by locating an appropriate virtual disk provider to accomplish the operation.
/// </summary>
public void Detach ( )
{
if ( ! Attached )
2017-11-27 13:11:20 -05:00
{
2022-04-11 17:46:20 -04:00
return ;
2017-11-27 13:11:20 -05:00
}
2022-04-11 17:46:20 -04:00
DetachVirtualDisk ( Handle , DETACH_VIRTUAL_DISK_FLAG . DETACH_VIRTUAL_DISK_FLAG_NONE , 0 ) . ThrowIfFailed ( ) ;
}
2022-03-11 20:07:36 -05:00
2022-04-11 17:46:20 -04:00
/// <inheritdoc/>
public virtual void Dispose ( ) = > Handle . Dispose ( ) ;
2019-10-04 08:55:01 -04:00
2022-04-11 17:46:20 -04:00
/// <summary>Increases the size of a fixed or dynamic virtual hard disk (VHD).</summary>
/// <param name="newSize">New size, in bytes, for the expansion request.</param>
public void Expand ( ulong newSize )
{
if ( ver < OPEN_VIRTUAL_DISK_VERSION . OPEN_VIRTUAL_DISK_VERSION_2 )
2022-03-10 23:59:26 -05:00
{
2022-04-11 17:46:20 -04:00
throw new NotSupportedException ( @"Expansion is only available to virtual disks opened under version 2 or higher." ) ;
2022-03-10 23:59:26 -05:00
}
2022-04-11 17:46:20 -04:00
ExpandVirtualDisk ( Handle , EXPAND_VIRTUAL_DISK_FLAG . EXPAND_VIRTUAL_DISK_FLAG_NONE , new EXPAND_VIRTUAL_DISK_PARAMETERS ( newSize ) , IntPtr . Zero ) . ThrowIfFailed ( ) ;
}
2022-03-10 23:59:26 -05:00
2022-04-11 17:46:20 -04:00
/// <summary>Increases the size of a fixed or dynamic virtual hard disk (VHD).</summary>
/// <param name="newSize">New size, in bytes, for the expansion request.</param>
/// <param name="cancellationToken">
/// A cancellation token that can be used to cancel the operation. This value can be <c>default</c> to disable cancellation.
/// </param>
/// <param name="progress">
/// A class that implements <see cref="IProgress{T}"/> that can be used to report on progress. This value can be <c>null</c> to disable
/// progress reporting.
/// </param>
/// <returns><c>true</c> if operation completed without error or cancellation; <c>false</c> otherwise.</returns>
2023-09-06 11:14:25 -04:00
public async Task < bool > ExpandAsync ( ulong newSize , CancellationToken cancellationToken = default , IProgress < int > ? progress = default ) = >
2022-09-20 21:29:19 -04:00
await RunAsync ( Handle , ( in NativeOverlapped vhdOverlap ) = >
2022-03-11 20:07:36 -05:00
{
2022-04-11 17:46:20 -04:00
if ( ver < OPEN_VIRTUAL_DISK_VERSION . OPEN_VIRTUAL_DISK_VERSION_2 )
{
throw new NotSupportedException ( @"Expansion is only available to virtual disks opened under version 2 or higher." ) ;
}
EXPAND_VIRTUAL_DISK_PARAMETERS param = new ( newSize ) ;
return ExpandVirtualDisk ( Handle , EXPAND_VIRTUAL_DISK_FLAG . EXPAND_VIRTUAL_DISK_FLAG_NONE , param , vhdOverlap ) ;
2022-09-20 21:29:19 -04:00
} , cancellationToken , progress ) ;
2022-03-11 20:07:36 -05:00
2022-04-11 17:46:20 -04:00
/// <summary>Issues an embedded SCSI request directly to a virtual hard disk.</summary>
/// <param name="param">A valid RAW_SCSI_VIRTUAL_DISK_PARAMETERS structure that contains snapshot deletion data.</param>
/// <returns>A RAW_SCSI_VIRTUAL_DISK_RESPONSE structure that contains the results of processing the SCSI command.</returns>
/// <exception cref="PlatformNotSupportedException">Only supported on Windows 10 and later.</exception>
public RAW_SCSI_VIRTUAL_DISK_RESPONSE IssueSCSIRequest ( in RAW_SCSI_VIRTUAL_DISK_PARAMETERS param )
{
if ( ! PInvokeClient . Windows10 . IsPlatformSupported ( ) )
throw new PlatformNotSupportedException ( "Only supported on Windows 10 and later." ) ;
RawSCSIVirtualDisk ( Handle , param , RAW_SCSI_VIRTUAL_DISK_FLAG . RAW_SCSI_VIRTUAL_DISK_FLAG_NONE , out RAW_SCSI_VIRTUAL_DISK_RESPONSE resp ) . ThrowIfFailed ( ) ;
return resp ;
}
/// <summary>Merges a child virtual hard disk (VHD) in a differencing chain with parent disks in the chain.</summary>
/// <param name="sourceDepth">Depth from the leaf from which to begin the merge. The leaf is at depth 1.</param>
/// <param name="targetDepth">Depth from the leaf to target the merge. The leaf is at depth 1.</param>
public void Merge ( uint sourceDepth , uint targetDepth )
{
MERGE_VIRTUAL_DISK_PARAMETERS param = new ( sourceDepth , targetDepth ) ;
MergeVirtualDisk ( Handle , MERGE_VIRTUAL_DISK_FLAG . MERGE_VIRTUAL_DISK_FLAG_NONE , param , IntPtr . Zero ) . ThrowIfFailed ( ) ;
}
/// <summary>Asynchronously merges a child virtual hard disk (VHD) in a differencing chain with parent disks in the chain.</summary>
/// <param name="sourceDepth">Depth from the leaf from which to begin the merge. The leaf is at depth 1.</param>
/// <param name="targetDepth">Depth from the leaf to target the merge. The leaf is at depth 1.</param>
/// <param name="cancellationToken">
/// A cancellation token that can be used to cancel the operation. This value can be <c>default</c> to disable cancellation.
/// </param>
/// <param name="progress">
/// A class that implements <see cref="IProgress{T}"/> that can be used to report on progress. This value can be <c>null</c> to disable
/// progress reporting.
/// </param>
2023-09-06 11:14:25 -04:00
public async Task MergeAsync ( uint sourceDepth , uint targetDepth , CancellationToken cancellationToken = default , IProgress < int > ? progress = default ) = >
2022-09-20 21:29:19 -04:00
await RunAsync ( Handle , ( in NativeOverlapped vhdOverlap ) = >
2022-04-11 17:46:20 -04:00
{
MERGE_VIRTUAL_DISK_PARAMETERS param = new ( sourceDepth , targetDepth ) ;
return MergeVirtualDisk ( Handle , MERGE_VIRTUAL_DISK_FLAG . MERGE_VIRTUAL_DISK_FLAG_NONE , param , vhdOverlap ) ;
2022-09-20 21:29:19 -04:00
} , cancellationToken , progress ) ;
2017-11-27 13:11:20 -05:00
2022-04-11 17:46:20 -04:00
/// <summary>Merges a child virtual hard disk (VHD) in a differencing chain with its immediate parent disk in the chain.</summary>
public void MergeWithParent ( )
{
MERGE_VIRTUAL_DISK_PARAMETERS param = new ( 1 ) ;
MergeVirtualDisk ( Handle , MERGE_VIRTUAL_DISK_FLAG . MERGE_VIRTUAL_DISK_FLAG_NONE , param , IntPtr . Zero ) . ThrowIfFailed ( ) ;
}
2017-11-27 13:11:20 -05:00
2022-04-11 17:46:20 -04:00
/// <summary>
/// Initiates a mirror operation for a virtual disk. Once the mirroring operation is initiated it will not complete until either CancelIo
/// or CancelIoEx is called to cancel all I/O on the VirtualDiskHandle, leaving the original file as the current or
/// BreakMirrorVirtualDisk is called to stop using the original file and only use the mirror.
/// </summary>
/// <param name="destPath">Fully qualified path where the mirrored virtual disk will be or is located.</param>
/// <param name="cancellationToken">
/// A cancellation token that can be used to cancel the operation. This value can be <c>default</c> to disable cancellation.
/// </param>
/// <param name="progress">
/// A class that implements <see cref="IProgress{T}"/> that can be used to report on progress. This value can be <c>null</c> to disable
/// progress reporting.
/// </param>
/// <exception cref="NotSupportedException">@"Mirroring is only available to virtual disks opened under version 2 or higher.</exception>
2023-09-06 11:14:25 -04:00
public async Task MirrorAsync ( string destPath , CancellationToken cancellationToken = default , IProgress < int > ? progress = default )
2022-04-11 17:46:20 -04:00
{
if ( ver < OPEN_VIRTUAL_DISK_VERSION . OPEN_VIRTUAL_DISK_VERSION_2 )
throw new NotSupportedException ( @"Mirroring is only available to virtual disks opened under version 2 or higher." ) ;
2022-09-20 21:29:19 -04:00
await RunAsync ( Handle , ( in NativeOverlapped vhdOverlap ) = >
2017-11-27 13:11:20 -05:00
{
2022-04-11 17:46:20 -04:00
MIRROR_VIRTUAL_DISK_FLAG flags = File . Exists ( destPath ) ? MIRROR_VIRTUAL_DISK_FLAG . MIRROR_VIRTUAL_DISK_FLAG_EXISTING_FILE : MIRROR_VIRTUAL_DISK_FLAG . MIRROR_VIRTUAL_DISK_FLAG_NONE ;
SafeCoTaskMemString pDest = new ( destPath ) ;
MIRROR_VIRTUAL_DISK_PARAMETERS param = new ( ) { Version = MIRROR_VIRTUAL_DISK_VERSION . MIRROR_VIRTUAL_DISK_VERSION_1 , Version1 = new ( ) { MirrorVirtualDiskPath = pDest } } ;
return MirrorVirtualDisk ( Handle , flags , param , vhdOverlap ) ;
2022-09-20 21:29:19 -04:00
} , cancellationToken , progress ) ;
2022-04-11 17:46:20 -04:00
}
2022-03-11 20:07:36 -05:00
2022-04-11 17:46:20 -04:00
/// <summary>Modifies the internal contents of a virtual disk file. Can be used to set the active leaf, or to fix up snapshot entries.</summary>
/// <param name="id">The Snapshot Id in GUID format indicating which snapshot is to have its path altered in the VHD Set.</param>
/// <param name="newSnapshotFilePath">The new file path for the Snapshot indicated by <paramref name="id"/>.</param>
/// <param name="writeable"><see langword="true"/> to indicate that the snapshot should be created as a writable snapshot type.</param>
public void ModifySnapshotPath ( Guid id , string newSnapshotFilePath , bool writeable = false )
{
using SafeCoTaskMemString pfp = new ( newSnapshotFilePath ) ;
MODIFY_VHDSET_PARAMETERS param = new ( )
{
Version = MODIFY_VHDSET_VERSION . MODIFY_VHDSET_SNAPSHOT_PATH ,
Version1 = new ( ) { SnapshotPath = new ( ) { SnapshotId = id , SnapshotFilePath = ( IntPtr ) pfp } }
} ;
ModifyVhdSet ( Handle , param , writeable ? 0 : MODIFY_VHDSET_FLAG . MODIFY_VHDSET_FLAG_WRITEABLE_SNAPSHOT ) . ThrowIfFailed ( ) ;
}
2017-11-27 13:11:20 -05:00
2022-04-11 17:46:20 -04:00
/// <summary>
/// Retrieves information about changes to the specified areas of a virtual hard disk (VHD) that are tracked by resilient change tracking (RCT).
/// </summary>
/// <param name="changeTrackingId">
/// The change tracking identifier for the change that identifies the state of the virtual disk that you want to use as the basis of
/// comparison to determine whether the specified area of the VHD has changed.
/// </param>
/// <param name="offset">
/// The distance from the start of the VHD to the beginning of the area of the VHD that you want to check for changes, in bytes.
/// </param>
/// <param name="length">The length of the area of the VHD that you want to check for changes, in bytes.</param>
/// <returns>
/// An array of QUERY_CHANGES_VIRTUAL_DISK_RANGE structures that indicates the areas of the virtual disk within the area that the
/// <paramref name="offset"/> and <paramref name="length"/> parameters specify that have changed since the change tracking identifier
/// that the <paramref name="changeTrackingId"/> parameter specifies was sealed.
/// </returns>
public QUERY_CHANGES_VIRTUAL_DISK_RANGE [ ] QueryChanges ( string changeTrackingId , ulong offset = 0 , ulong length = ulong . MaxValue )
{
if ( length + offset > VirtualSize )
length = VirtualSize - offset ;
2017-11-27 13:11:20 -05:00
2022-04-11 17:46:20 -04:00
var cRanges = 42 u ;
ulong cProcessed ;
QUERY_CHANGES_VIRTUAL_DISK_RANGE [ ] ranges ;
do
{
cRanges * = 4 ;
ranges = new QUERY_CHANGES_VIRTUAL_DISK_RANGE [ cRanges ] ;
QueryChangesVirtualDisk ( Handle , changeTrackingId , offset , length , QUERY_CHANGES_VIRTUAL_DISK_FLAG . QUERY_CHANGES_VIRTUAL_DISK_FLAG_NONE ,
ranges , ref cRanges , out cProcessed ) . ThrowIfFailed ( ) ;
} while ( cProcessed < length ) ;
Array . Resize ( ref ranges , ( int ) cRanges ) ;
return ranges ;
}
2022-03-11 20:07:36 -05:00
2022-04-11 17:46:20 -04:00
/// <summary>Resizes a virtual disk.</summary>
/// <param name="newSize">
/// New size, in bytes, for the expansion request. Setting this value to '0' will shrink the disk to the smallest safe virtual size
/// possible without truncating past any existing partitions.
/// </param>
public void Resize ( ulong newSize )
{
if ( ver < OPEN_VIRTUAL_DISK_VERSION . OPEN_VIRTUAL_DISK_VERSION_2 )
{
throw new NotSupportedException ( @"Expansion is only available to virtual disks opened under version 2 or higher." ) ;
2017-11-27 13:11:20 -05:00
}
2022-04-11 17:46:20 -04:00
RESIZE_VIRTUAL_DISK_FLAG flags = newSize = = 0 ? RESIZE_VIRTUAL_DISK_FLAG . RESIZE_VIRTUAL_DISK_FLAG_RESIZE_TO_SMALLEST_SAFE_VIRTUAL_SIZE : RESIZE_VIRTUAL_DISK_FLAG . RESIZE_VIRTUAL_DISK_FLAG_NONE ;
RESIZE_VIRTUAL_DISK_PARAMETERS param = new ( newSize ) ;
ResizeVirtualDisk ( Handle , flags , param , IntPtr . Zero ) . ThrowIfFailed ( ) ;
}
2022-03-11 20:07:36 -05:00
2022-04-11 17:46:20 -04:00
/// <summary>Resizes a virtual disk.</summary>
/// <param name="newSize">New size, in bytes, for the expansion request.</param>
/// <param name="cancellationToken">
/// A cancellation token that can be used to cancel the operation. This value can be <c>default</c> to disable cancellation.
/// </param>
/// <param name="progress">
/// A class that implements <see cref="IProgress{T}"/> that can be used to report on progress. This value can be <c>null</c> to disable
/// progress reporting.
/// </param>
/// <returns><c>true</c> if operation completed without error or cancellation; <c>false</c> otherwise.</returns>
2023-09-06 11:14:25 -04:00
public async Task < bool > ResizeAsync ( ulong newSize , CancellationToken cancellationToken = default , IProgress < int > ? progress = default )
2022-04-11 17:46:20 -04:00
{
if ( ver < OPEN_VIRTUAL_DISK_VERSION . OPEN_VIRTUAL_DISK_VERSION_2 )
{
throw new NotSupportedException ( @"Expansion is only available to virtual disks opened under version 2 or higher." ) ;
2017-11-27 13:11:20 -05:00
}
2022-09-20 21:29:19 -04:00
return await RunAsync ( Handle , ( in NativeOverlapped vhdOverlap ) = >
2017-11-27 13:11:20 -05:00
{
2022-04-11 17:46:20 -04:00
RESIZE_VIRTUAL_DISK_PARAMETERS param = new ( newSize ) ;
return ResizeVirtualDisk ( Handle , RESIZE_VIRTUAL_DISK_FLAG . RESIZE_VIRTUAL_DISK_FLAG_NONE , param , vhdOverlap ) ;
2022-09-20 21:29:19 -04:00
} , cancellationToken , progress ) ;
2022-04-11 17:46:20 -04:00
}
2017-11-27 13:11:20 -05:00
2022-04-11 17:46:20 -04:00
/// <summary>Sets the active leaf of a virtual disk file.</summary>
/// <param name="defaultFilePath">The file path for the default Snapshot of the Vhd Set.</param>
/// <param name="writeable"><see langword="true"/> to indicate that the snapshot should be created as a writable snapshot type.</param>
public void SetDefaultSnapshotPath ( string defaultFilePath , bool writeable = false )
{
using SafeCoTaskMemString pfp = new ( defaultFilePath ) ;
MODIFY_VHDSET_PARAMETERS param = new ( )
{
Version = MODIFY_VHDSET_VERSION . MODIFY_VHDSET_DEFAULT_SNAPSHOT_PATH ,
Version1 = new ( ) { DefaultFilePath = ( IntPtr ) pfp }
} ;
ModifyVhdSet ( Handle , param , writeable ? 0 : MODIFY_VHDSET_FLAG . MODIFY_VHDSET_FLAG_WRITEABLE_SNAPSHOT ) . ThrowIfFailed ( ) ;
}
2017-11-27 13:11:20 -05:00
2022-04-11 17:46:20 -04:00
/// <summary>Sets the path to virtal hard disk's parent.</summary>
/// <param name="path">The full path to the parent disk.</param>
public void SetParentPath ( string path )
{
using SafeCoTaskMemString pStr = new ( path ) ;
SET_VIRTUAL_DISK_INFO si = new ( SET_VIRTUAL_DISK_INFO_VERSION . SET_VIRTUAL_DISK_INFO_PARENT_PATH ) { ParentFilePath = ( IntPtr ) pStr } ;
SetVirtualDiskInformation ( Handle , si ) . ThrowIfFailed ( ) ;
}
2017-11-27 13:11:20 -05:00
2022-04-11 17:46:20 -04:00
/// <summary>Sets the path to virtal hard disk's parent and the child depth.</summary>
/// <param name="path">The full path to the parent disk.</param>
/// <param name="childDepth">Specifies the depth to the child from the leaf. The leaf itself is at depth 1.</param>
public void SetParentPathAndDepth ( string path , uint childDepth )
{
using SafeCoTaskMemString pStr = new ( path ) ;
SET_VIRTUAL_DISK_INFO si = new ( SET_VIRTUAL_DISK_INFO_VERSION . SET_VIRTUAL_DISK_INFO_PARENT_PATH_WITH_DEPTH )
{ ParentPathWithDepthInfo = new ( ) { ParentFilePath = ( IntPtr ) pStr , ChildDepth = childDepth } } ;
SetVirtualDiskInformation ( Handle , si ) . ThrowIfFailed ( ) ;
}
2017-11-27 13:11:20 -05:00
2022-04-11 17:46:20 -04:00
/// <summary>Creates a snapshot of the current virtual disk for VHD Set files.</summary>
/// <param name="id">The Id of the new Snapshot to be added to the Vhd Set.</param>
/// <param name="writeable"><see langword="true"/> to indicate that the snapshot should be created as a writable snapshot type.</param>
public void TakeSnapshot ( Guid id , bool writeable = false )
{
TAKE_SNAPSHOT_VHDSET_PARAMETERS param = new ( )
{
Version = TAKE_SNAPSHOT_VHDSET_VERSION . TAKE_SNAPSHOT_VHDSET_VERSION_1 ,
Version1 = new ( ) { SnapshotId = id }
} ;
TakeSnapshotVhdSet ( Handle , param , writeable ? 0 : TAKE_SNAPSHOT_VHDSET_FLAG . TAKE_SNAPSHOT_VHDSET_FLAG_WRITEABLE ) . ThrowIfFailed ( ) ;
}
2017-11-27 13:11:20 -05:00
2022-04-11 17:46:20 -04:00
/// <summary>
/// Resizes a virtual disk without checking the virtual disk's partition table to ensure that this truncation is safe. <note
/// type="warning">This method can cause unrecoverable data loss; use with care.</note>
/// </summary>
/// <param name="newSize">New size, in bytes, for the expansion request.</param>
public void UnsafeResize ( ulong newSize )
{
if ( ver < OPEN_VIRTUAL_DISK_VERSION . OPEN_VIRTUAL_DISK_VERSION_2 )
{
throw new NotSupportedException ( @"Expansion is only available to virtual disks opened under version 2 or higher." ) ;
}
2017-11-27 13:11:20 -05:00
2022-04-11 17:46:20 -04:00
RESIZE_VIRTUAL_DISK_FLAG flags = RESIZE_VIRTUAL_DISK_FLAG . RESIZE_VIRTUAL_DISK_FLAG_ALLOW_UNSAFE_VIRTUAL_SIZE ;
RESIZE_VIRTUAL_DISK_PARAMETERS param = new ( newSize ) ;
ResizeVirtualDisk ( Handle , flags , param , IntPtr . Zero ) . ThrowIfFailed ( ) ;
}
2017-11-27 13:11:20 -05:00
2023-09-06 11:14:25 -04:00
private static SafePSECURITY_DESCRIPTOR FileSecToSd ( FileSecurity ? sec ) = > sec = = null
2022-04-11 17:46:20 -04:00
? SafePSECURITY_DESCRIPTOR . Null
: ConvertStringSecurityDescriptorToSecurityDescriptor ( sec . GetSecurityDescriptorSddlForm ( AccessControlSections . All ) ) ;
2017-11-27 13:11:20 -05:00
2022-04-11 17:46:20 -04:00
private static uint? GetDiskNumberFromDevicePath ( string devicePath )
{
using var hfile = OpenDrive ( devicePath ) ;
return ! hfile . IsInvalid & & DeviceIoControl ( hfile , IOControlCode . IOCTL_STORAGE_GET_DEVICE_NUMBER , out STORAGE_DEVICE_NUMBER output )
? output . DeviceNumber : null ;
}
2017-11-27 13:11:20 -05:00
2023-09-06 11:14:25 -04:00
private static bool GetProgress ( VIRTUAL_DISK_HANDLE hVhd , in NativeOverlapped vhdOverlap , CancellationToken cancellationToken , IProgress < int > ? progress )
2022-04-11 17:46:20 -04:00
{
progress ? . Report ( 0 ) ;
while ( true )
{
if ( cancellationToken . IsCancellationRequested )
return false ;
2017-11-27 13:11:20 -05:00
2022-09-20 21:29:19 -04:00
GetVirtualDiskOperationProgress ( hVhd , vhdOverlap , out VIRTUAL_DISK_PROGRESS prog ) . ThrowIfFailed ( ) ;
2022-04-11 17:46:20 -04:00
switch ( prog . OperationStatus )
{
case 0 :
progress ? . Report ( 100 ) ;
return true ;
2017-12-22 11:09:04 -05:00
2022-04-11 17:46:20 -04:00
case Win32Error . ERROR_IO_PENDING :
2022-09-26 11:57:49 -04:00
progress ? . Report ( Math . Min ( 100 , ( int ) ( prog . CurrentValue * 100 / prog . CompletionValue ) ) ) ;
2022-04-11 17:46:20 -04:00
break ;
2021-06-01 13:53:58 -04:00
2022-04-11 17:46:20 -04:00
default :
2023-09-06 11:14:25 -04:00
throw new Win32Error ( prog . OperationStatus ) . GetException ( ) ? ? new Exception ( ) ;
2021-06-01 13:53:58 -04:00
}
2022-09-20 21:29:19 -04:00
Task . Delay ( 100 , cancellationToken ) ;
2021-06-01 13:53:58 -04:00
}
2022-04-11 17:46:20 -04:00
}
2021-06-01 13:53:58 -04:00
2023-09-06 11:14:25 -04:00
private static string? GetVolumeGuidFromDiskNumber ( uint diskNo ) = > GetVolumeGuidsFromDiskNumber ( diskNo ) . FirstOrDefault ( ) ;
2022-05-03 18:49:12 -04:00
private static IEnumerable < string > GetVolumeGuidsFromDiskNumber ( uint diskNo ) = > EnumVolumes ( ) . Where ( v = >
{
using var hfile = OpenDrive ( v . TrimEnd ( '\\' ) ) ;
return ! hfile . IsInvalid & &
DeviceIoControl ( hfile , IOControlCode . IOCTL_STORAGE_GET_DEVICE_NUMBER , out STORAGE_DEVICE_NUMBER output ) & &
output . DeviceNumber = = diskNo ;
} ) . ToArray ( ) ;
2022-04-11 17:46:20 -04:00
/// <summary>Retrieves a list of drive letters and mounted folder paths for the specified volume.</summary>
/// <param name="volumeName">A volume GUID path for the volume. A volume GUID path is of the form "\\?\Volume{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}\".</param>
/// <returns>The list of drive letters and mounted folder paths.</returns>
private static string [ ] GetVolumeMountPoints ( string volumeName )
{
bool ret = GetVolumePathNamesForVolumeName ( volumeName , IntPtr . Zero , 0 , out var sz ) ;
var err = Win32Error . GetLastError ( ) ;
2023-09-06 11:14:25 -04:00
if ( ! ret & & err ! = Win32Error . ERROR_MORE_DATA ) throw err . GetException ( ) ! ;
2022-04-11 17:46:20 -04:00
if ( ret ) return new string [ 0 ] ;
using var mem = new SafeCoTaskMemHandle ( sz * StringHelper . GetCharSize ( ) ) ;
Win32Error . ThrowLastErrorIfFalse ( GetVolumePathNamesForVolumeName ( volumeName , mem , sz , out _ ) ) ;
return mem . ToStringEnum ( ) . ToArray ( ) ;
}
private static SafeHFILE OpenDrive ( string fn ) = > CreateFile ( fn , 0 , FileShare . ReadWrite , default , FileMode . Open , 0 ) ;
2023-09-06 11:14:25 -04:00
private static async Task < bool > RunAsync ( VIRTUAL_DISK_HANDLE hVhd , RunAsyncMethod method , CancellationToken cancellationToken , IProgress < int > ? progress ) = >
2022-09-20 21:29:19 -04:00
await Task . Run ( ( ) = >
2021-06-01 13:53:58 -04:00
{
2022-09-20 21:29:19 -04:00
NativeOverlapped vhdOverlap = new ( ) ;
method ( in vhdOverlap ) . ThrowUnless ( Win32Error . ERROR_IO_PENDING ) ;
return GetProgress ( hVhd , vhdOverlap , cancellationToken , progress ) ;
} ) ;
2022-04-11 17:46:20 -04:00
private T GetInformation < T > ( GET_VIRTUAL_DISK_INFO_VERSION info , long offset = 0 )
{
using SafeCoTaskMemStruct < GET_VIRTUAL_DISK_INFO > mem = GetInformation ( info ) ;
2017-12-22 11:09:04 -05:00
2022-04-11 17:46:20 -04:00
offset + = 8 ; // Add size of version
if ( typeof ( T ) = = typeof ( string [ ] ) )
{
if ( mem . DangerousGetHandle ( ) . Offset ( offset ) . ToStructure < ushort > ( ) = = 0 )
2017-12-22 11:09:04 -05:00
{
2022-04-11 17:46:20 -04:00
return ( T ) ( object ) new string [ 0 ] ;
2017-12-22 11:09:04 -05:00
}
2022-04-11 17:46:20 -04:00
return ( T ) ( object ) mem . DangerousGetHandle ( ) . ToStringEnum ( CharSet . Unicode , ( int ) offset ) . ToArray ( ) ;
}
2022-03-11 20:07:36 -05:00
2023-09-06 11:14:25 -04:00
return mem . DangerousGetHandle ( ) . Offset ( offset ) . Convert < T > ( ( uint ) ( mem . Size - offset ) , CharSet . Unicode ) ! ;
2022-04-11 17:46:20 -04:00
}
2017-12-22 11:09:04 -05:00
2022-04-11 17:46:20 -04:00
private SafeCoTaskMemStruct < GET_VIRTUAL_DISK_INFO > GetInformation ( GET_VIRTUAL_DISK_INFO_VERSION info )
{
SafeCoTaskMemStruct < GET_VIRTUAL_DISK_INFO > mem = new ( new GET_VIRTUAL_DISK_INFO { Version = info } ) ;
uint sz = mem . Size ;
Win32Error err = GetVirtualDiskInformation ( Handle , ref sz , mem , out var req ) ;
if ( err = = Win32Error . ERROR_INSUFFICIENT_BUFFER )
{
mem . Size = sz ;
err = GetVirtualDiskInformation ( Handle , ref sz , mem , out req ) ;
}
Debug . WriteLineIf ( err . Succeeded , $"GetVirtualDiskInformation: Id={info.ToString().Remove(0, 22)}; Unk={Marshal.ReadInt32(mem, 4)}; Sz={req}; Bytes={string.Join(" ", mem.DangerousGetHandle().ToIEnum<uint>((int)req / 4).Select(b => b.ToString(" X8 ")).ToArray())}" ) ;
err . ThrowIfFailed ( ) ;
return mem ;
}
2017-12-22 11:09:04 -05:00
2022-04-11 17:46:20 -04:00
/// <summary>Supports getting and setting metadata on a virtual disk.</summary>
/// <seealso cref="IDictionary{Guid, SafeCoTaskMemHandle}"/>
public class VirtualDiskMetadata : Vanara . Collections . VirtualDictionary < Guid , SafeCoTaskMemHandle >
{
private readonly VirtualDisk parent ;
private readonly bool supported ;
2017-12-22 11:09:04 -05:00
2022-04-11 17:46:20 -04:00
/// <summary>Initializes a new instance of the <see cref="VirtualDiskMetadata"/> class.</summary>
/// <param name="vhd">The VHD.</param>
internal VirtualDiskMetadata ( VirtualDisk vhd ) : base ( false )
{
parent = vhd ;
supported = vhd . DiskType = = DeviceType . Vhdx ;
}
/// <inheritdoc/>
public override ICollection < Guid > Keys
{
get
2017-12-22 11:09:04 -05:00
{
2022-04-11 17:46:20 -04:00
var ret = new Guid [ 0 ] ;
if ( supported )
2017-12-22 11:09:04 -05:00
{
2022-03-11 20:07:36 -05:00
if ( parent . Handle . IsClosed )
{
throw new InvalidOperationException ( "Virtual disk not valid." ) ;
}
2022-04-11 17:46:20 -04:00
uint count = 0 ;
Win32Error err = EnumerateVirtualDiskMetadata ( parent . Handle , ref count , null ) ;
2022-03-11 20:07:36 -05:00
if ( err ! = Win32Error . ERROR_MORE_DATA & & err ! = Win32Error . ERROR_INSUFFICIENT_BUFFER )
{
err . ThrowIfFailed ( ) ;
}
2022-04-11 17:46:20 -04:00
if ( count ! = 0 )
2022-03-11 20:07:36 -05:00
{
2022-04-11 17:46:20 -04:00
ret = new Guid [ count ] ;
EnumerateVirtualDiskMetadata ( parent . Handle , ref count , ret ) . ThrowIfFailed ( ) ;
return ret ;
2022-03-11 20:07:36 -05:00
}
2017-12-22 11:09:04 -05:00
}
2022-04-11 17:46:20 -04:00
return ret ;
2017-12-22 11:09:04 -05:00
}
2022-04-11 17:46:20 -04:00
}
2017-12-22 11:09:04 -05:00
2022-04-11 17:46:20 -04:00
/// <inheritdoc/>
public override bool Remove ( Guid key )
{
ThrowIfInvalid ( ) ;
2017-12-22 11:09:04 -05:00
2022-04-11 17:46:20 -04:00
return DeleteVirtualDiskMetadata ( parent . Handle , key ) . Succeeded ;
}
2022-03-11 20:07:36 -05:00
2022-04-11 17:46:20 -04:00
/// <inheritdoc/>
public override bool TryGetValue ( Guid key , out SafeCoTaskMemHandle value )
{
ThrowIfInvalid ( ) ;
2022-03-11 20:07:36 -05:00
2022-04-11 17:46:20 -04:00
uint sz = 0 ;
value = SafeCoTaskMemHandle . Null ;
Win32Error err = GetVirtualDiskMetadata ( parent . Handle , key , ref sz , default ) ;
if ( err ! = Win32Error . ERROR_MORE_DATA & & err ! = Win32Error . ERROR_INSUFFICIENT_BUFFER )
return false ;
SafeCoTaskMemHandle ret = new ( ( int ) sz ) ;
if ( GetVirtualDiskMetadata ( parent . Handle , key , ref sz , ret ) . Failed )
return false ;
value = ret ;
return true ;
}
2017-12-22 11:09:04 -05:00
2022-04-11 17:46:20 -04:00
/// <inheritdoc/>
protected override void SetValue ( Guid key , SafeCoTaskMemHandle value )
{
ThrowIfInvalid ( ) ;
2017-12-22 11:09:04 -05:00
2022-04-11 17:46:20 -04:00
SetVirtualDiskMetadata ( parent . Handle , key , value . Size , value ) . ThrowIfFailed ( ) ;
2017-12-22 11:09:04 -05:00
}
2017-12-22 11:47:09 -05:00
2022-04-11 17:46:20 -04:00
private void ThrowIfInvalid ( )
{
if ( ! supported )
throw new PlatformNotSupportedException ( ) ;
if ( parent . Handle . IsClosed )
throw new InvalidOperationException ( "Virtual disk not valid." ) ;
}
2017-11-27 13:11:20 -05:00
}
}