{"id":96555,"date":"2017-07-07T07:00:00","date_gmt":"2017-07-07T21:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/?p=96555"},"modified":"2019-03-13T01:13:55","modified_gmt":"2019-03-13T08:13:55","slug":"20170707-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20170707-00\/?p=96555","title":{"rendered":"How can I tell whether two paths reside on the same underlying volume, so I can know whether they can be hard-linked?"},"content":{"rendered":"<p>A customer wanted to know whether there was a way to determine whether two paths reside on the same underlying volume. They were concerned that due to tricks like <code>SUBST<\/code> and volume mount points, the na&iuml;ve algorithm of comparing drive letters may prove inadequate. <\/p>\n<p>You can use <code>Get&shy;File&shy;Information&shy;By&shy;Handle<\/code> to get the volume serial number and the file index. Even better is to call <code>Get&shy;File&shy;Information&shy;By&shy;Handle&shy;Ex<\/code> with the <code>File&shy;Id&shy;Info<\/code> information code, because ReFS use a 64-bit volume identifier and a 128-bit file identifier (double the size of NTFS). <\/p>\n<p>There is a small chance that two volumes might happen to have the same volume identifier, seeing as a 32-bit value (or a 64-bit value in the case of ReFS) is not quite enough to guarantee global uniqueness. You can try to filter out that case by using <code>Open&shy;File&shy;By&shy;Id<\/code> and passing the first file&#8217;s file handle and the second file&#8217;s ID. The <code>Open&shy;File&shy;By&shy;Id<\/code> uses the file handle to decide what volume to use, so passing the first file&#8217;s file handle means &#8220;Open the new file from the same volume that the old file is on.&#8221; You can then compare the newly-opened file&#8217;s <a HREF=\"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/20110228-00\/?p=11363\">object GUID<\/a> against the object GUID of the intended second file and see if they match. <\/p>\n<p>Actually, I guess you could have gone straight for the object GUID from the outset: Obtain the object GUID for the second file, then try to open the file by its GUID (relative to the first file). <\/p>\n<p>But wait, we&#8217;re so distracted by answering the customer&#8217;s question that we don&#8217;t understand the customer&#8217;s <i>problem<\/i>. <\/p>\n<p>The customer explained that they want to create a content-addressable database, but if they find that there&#8217;s already a database on the same volume, then they can just hard-link the contents together and save disk space. <\/p>\n<p>Oh, so the problem isn&#8217;t &#8220;Determine whether two files are on the same volume (so I can hard-link them).&#8221; The problem is &#8220;Determine whether two files can be hard-linked.&#8221; <\/p>\n<p>That problem is much easier to solve. The way to determine whether two files can be hard-linked is to try hard-linking them and seeing if it works. As <a HREF=\"https:\/\/blogs.msdn.microsoft.com\/jaredpar\/\">Jared Parsons<\/a> noted, <a HREF=\"https:\/\/blogs.msdn.microsoft.com\/jaredpar\/2009\/12\/10\/the-file-system-is-unpredictable\/\">the file system is unpredictable<\/a>. <\/p>\n<blockquote CLASS=\"q\"><p>Do the operations you want and deal with the consequences of failure [&#8230;]. To do anything else involves an unreliable prediction in which you still must handle the resulting [failure]. <\/p><\/blockquote>\n<p>(Jared&#8217;s article is written for a C# audience, so he talks about exceptions, because that&#8217;s the C# paradigm for error handling.) <\/p>\n","protected":false},"excerpt":{"rendered":"<p>Don&#8217;t try to guess. Just do it.<\/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-96555","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Don&#8217;t try to guess. Just do it.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/96555","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=96555"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/96555\/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=96555"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=96555"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=96555"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}