Monthly Archives: February 2015

PowerShell: Getting the Disk Drive from a Volume or Mount Point


So this is way harder than I thought it would be.

If you take a look at the WMI class for Win32_MountPoint

https://msdn.microsoft.com/en-us/library/aa394205%28v=vs.85%29.aspx

You’ll notice there is a remark there that says:

There may not be any way to search from Win32_MountPoint to get to the associated Win32_DiskDrive

Which also applies to the Volume.

That sends you over to this blog post which talks about a win32 api call via DeviceIoControl and I managed to find decent info on PInvoke.net and also this blog post here which basically gives some VB code for it.

So after taking all that in, I was able to write a simple C# snippet that you can toss in PS to query for a Volume

Add-Type -TypeDefinition @"
using System;
using Microsoft.Win32.SafeHandles;
using System.IO;
using System.Runtime.InteropServices;

public class GetDisk
{
 private const uint IoctlVolumeGetVolumeDiskExtents = 0x560000;

 [StructLayout(LayoutKind.Sequential)]
 public struct DiskExtent
 {
 public int DiskNumber;
 public Int64 StartingOffset;
 public Int64 ExtentLength;
 }

 [StructLayout(LayoutKind.Sequential)]
 public struct DiskExtents
 {
 public int numberOfExtents;
 public DiskExtent first;
 }

 [DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
 private static extern SafeFileHandle CreateFile(
 string lpFileName,
 [MarshalAs(UnmanagedType.U4)] FileAccess dwDesiredAccess,
 [MarshalAs(UnmanagedType.U4)] FileShare dwShareMode,
 IntPtr lpSecurityAttributes,
 [MarshalAs(UnmanagedType.U4)] FileMode dwCreationDisposition,
 [MarshalAs(UnmanagedType.U4)] FileAttributes dwFlagsAndAttributes,
 IntPtr hTemplateFile);

 [DllImport("Kernel32.dll", SetLastError = false, CharSet = CharSet.Auto)]
 private static extern bool DeviceIoControl(
 SafeFileHandle hDevice,
 uint IoControlCode,
 [MarshalAs(UnmanagedType.AsAny)] [In] object InBuffer,
 uint nInBufferSize,
 ref DiskExtents OutBuffer,
 int nOutBufferSize,
 ref uint pBytesReturned,
 IntPtr Overlapped
);

 public static string GetPhysicalDriveString(string path)
 {
 //clean path up
 path = path.TrimEnd('\\');
 if (!path.StartsWith(@"\\.\"))
 path = @"\\.\" + path;

 SafeFileHandle shwnd = CreateFile(path, FileAccess.Read, FileShare.Read | FileShare.Write, IntPtr.Zero, FileMode.Open, 0,
 IntPtr.Zero);
 if (shwnd.IsInvalid)
 {
 //Marshal.ThrowExceptionForHR(Marshal.GetLastWin32Error());
 Exception e = Marshal.GetExceptionForHR(Marshal.GetLastWin32Error());
 }

 var bytesReturned = new uint();
 var de1 = new DiskExtents();
 bool result = DeviceIoControl(shwnd, IoctlVolumeGetVolumeDiskExtents, IntPtr.Zero, 0, ref de1,
 Marshal.SizeOf(de1), ref bytesReturned, IntPtr.Zero);
 shwnd.Close();
 if(result)
 return @"\\.\PhysicalDrive" + de1.first.DiskNumber;
 return null;
 }
}

"@

This will allow you to call this static method to get its Disk path, so you can do something like this.


$DeviceID = [GetDisk]::GetPhysicalDriveString('c:\')
gwmi win32_diskdrive | ? { $_.deviceid -eq $DeviceID}

Now you say, but Justin, this isnt working for a mount point path!? Which is true, but we can work around that.

We can use the Win32_MountPoint class to figure this out, or, you can use this cmdlet Get-MountPointData which basically parses the class for you. Really all you need to do is get the Volume DeviceID, which looks something like this.

Volume{ccbca757-b6c9-11e4-8275-448a5ba2d884}

So we can take that and search on it like so.


[GetDisk]::GetPhysicalDriveString('Volume{78498ddf-9a97-11e4-8252-806e6f6e6963}')

NOTE: This must be run as admin, since we’re hitting the disk subsystem (kernel)

Enjoy!

Advertisements