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

About jrich

I am the Solutions Architect for Apex Learning in Seattle WA. I've been working with computers since I was 13. Started programming when I was 14. Had my first IT job as tech support at an ISP at the age of 15 and became a network admin at the age of 17. Since then I've worked at a variety of small to mid size companies supporting, maintaining and developing all aspects of IT. Mostly working with Windows based networks but have recently been working with Solaris system as well. I created this blog mostly as a place for me to take my own notes, but also share things that I had a hard time finding the info for.

Posted on February 27, 2015, in .NET, C#, WMF (Powershell/WinRM) and tagged , , , , , , , , . Bookmark the permalink. 4 Comments.

  1. Can someone tell me how to adapt this so it returns all drives? I have some SQL servers that have up to 100 drives attached. It would be nice to get all the drives then match them up rather running this over and over against a remote server.

    • Since this function works off of drive letters you could use something like

      get-psdrive -PSProvider FileSystem | select -exp root

      If you’re not creating any crazy psdrives this will be fine, but you might want to filter it a bit to make sure the Root makes sense. or you can use wmi to pull win32_volume info and grab the name property

      once you have the list of volume names you just feed it in to that function

  2. Absolutely outstanding job! It allows getting scsibus,scsilogicalunit by providing MountPoint Name/Label :) Thank you very much!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: