{"id":104385,"date":"2020-10-20T07:00:00","date_gmt":"2020-10-20T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=104385"},"modified":"2020-10-20T08:12:28","modified_gmt":"2020-10-20T15:12:28","slug":"20201020-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20201020-00\/?p=104385","title":{"rendered":"How do I get from a file path to the volume that holds it?"},"content":{"rendered":"<p>Say you have the path to a file and you want to access the volume that the file resides on.<\/p>\n<p><b>Warning<\/b>: All error checking is removed for expository purposes.<\/p>\n<p>The first step on our journey is getting from the path to the volume mount point. This tells us where the root of the volume got inserted into the namespace.<\/p>\n<pre>TCHAR volumePath[MAX_PATH]; \/\/ for expository purposes\r\nGetVolumePathName(filePath, volumePath, ARRAYSIZE(volumePath));\r\n<\/pre>\n<p>This information might be useful in its own right, but for us, it&#8217;s just a stepping stone to the next piece of information: The volume name.<\/p>\n<pre>TCHAR volumeName[MAX_PATH]; \/\/ for expository purposes\r\nGetVolumeNameForVolumeMountPoint(volumePath, volumeName, ARRAYSIZE(volumeName));\r\n<\/pre>\n<p>The volume name is returned in the form <tt>\\\\?\\Volume{guid}\\<\/tt>, with a trailing backslash. Note that this call will fail if the path is not a local drive.<\/p>\n<p>Now things get weird.<\/p>\n<p>If you pass that path to the <code>CreateFile<\/code> function with the trailing backslash intact, then you are opening a handle to the root directory of the volume.<\/p>\n<p>If you pass that path to the <code>CreateFile<\/code> function with the trailing backslash removed, then you are opening a handle to the volume itself.<\/p>\n<p>Depending on which operation you want to perform on the volume, you either must have or must not have that trailing backslash.<\/p>\n<p>In our case, we want to open a handle to the volume itself, so we need to remove that trailing backslash. The call to <code>CreateFile<\/code> looks like this:<\/p>\n<pre>HANDLE handle = CreateFile(volumeNameWithoutTrailingBackslash,\r\n    0, \/* no special access requested *\/\r\n    FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,\r\n    nullptr, \/* no custom security *\/\r\n    OPEN_EXISTING,\r\n    FILE_FLAG_BACKUP_SEMANTICS,\r\n    nullptr); \/* template *\/\r\n<\/pre>\n<p>Volume query operations do not require any specific level of access, so we&#8217;ll ask for no special access. Which is good, because regular non-elevated code doesn&#8217;t really have much in the way of special access to volumes. You won&#8217;t able to get <code>GENERIC_<\/code><code>READ<\/code>, much less <code>GENERIC_<\/code><code>WRITE<\/code>. (You&#8217;ll be able to get <code>FILE_<\/code><code>READ_<\/code><code>ATTRIBUTES<\/code>, if that&#8217;s any consolation.)<\/p>\n<p>You also need to request backup semantics in order to open a volume.<\/p>\n<p>We can put all of this together into a function called, say, <code>Get\u00adVolume\u00adHandle\u00adFor\u00adFile<\/code>. For RAII, I&#8217;m going to use <a href=\"https:\/\/github.com\/microsoft\/wil\/\">wil<\/a>.<\/p>\n<pre>wil::unique_hfile GetVolumeHandleForFile(PCWSTR filePath)\r\n{\r\n  wchar_t volumePath[MAX_PATH];\r\n  THROW_IF_WIN32_BOOL_FALSE(GetVolumePathName(filePath,\r\n                                volumePath, ARRAYSIZE(volumePath)));\r\n\r\n  wchar_t volumeName[MAX_PATH];\r\n  THROW_IF_WIN32_BOOL_FALSE(GetVolumeNameForVolumeMountPoint(volumePath,\r\n                                volumeName, ARRAYSIZE(volumeName)));\r\n\r\n  auto length = wcslen(volumeName);\r\n  if (length &amp;&amp; volumeName[length - 1] == L'\\\\')\r\n  {\r\n    volumeName[length - 1] = L'\\0';\r\n  }\r\n\r\n  wil::unique_hfile result{ CreateFile(volumeName, 0,\r\n                FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,\r\n                nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr) };\r\n  THROW_LAST_ERROR_IF(!result);\r\n  return result;\r\n}\r\n<\/pre>\n<p>Now that you have the volume handle, you can ask the volume for information. We&#8217;ll look at that next time.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Walking down the topology, with a little backslash weirdness thrown in.<\/p>\n","protected":false},"author":1069,"featured_media":111744,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[25],"class_list":["post-104385","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Walking down the topology, with a little backslash weirdness thrown in.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/104385","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/users\/1069"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/comments?post=104385"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/104385\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media\/111744"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media?parent=104385"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=104385"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=104385"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}