How can I tell whether two paths reside on the same underlying volume, so I can know whether they can be hard-linked?


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 SUBST and volume mount points, the naïve algorithm of comparing drive letters may prove inadequate.

You can use Get­File­Information­By­Handle to get the volume serial number and the file index. Even better is to call Get­File­Information­By­Handle­Ex with the File­Id­Info information code, because ReFS use a 64-bit volume identifier and a 128-bit file identifier (double the size of NTFS).

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 Open­File­By­Id and passing the first file's file handle and the second file's ID. The Open­File­By­Id uses the file handle to decide what volume to use, so passing the first file's file handle means "Open the new file from the same volume that the old file is on." You can then compare the newly-opened file's object GUID against the object GUID of the intended second file and see if they match.

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).

But wait, we're so distracted by answering the customer's question that we don't understand the customer's problem.

The customer explained that they want to create a content-addressable database, but if they find that there's already a database on the same volume, then they can just hard-link the contents together and save disk space.

Oh, so the problem isn't "Determine whether two files are on the same volume (so I can hard-link them)." The problem is "Determine whether two files can be hard-linked."

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 Jared Parsons noted, the file system is unpredictable.

Do the operations you want and deal with the consequences of failure [...]. To do anything else involves an unreliable prediction in which you still must handle the resulting [failure].

(Jared's article is written for a C# audience, so he talks about exceptions, because that's the C# paradigm for error handling.)

Comments (24)
  1. DWalker07 says:

    It's amazing how much simpler things become when you think about them in the right way.

    Even if you write the code to determine if the paths reside on the same volume, you'll still have code to try to hard-link the files. And you should handle any exception or failure form that hard-link attempt anyway. Just try the hard-linking!

    And there may be other reasons, now or in the future, why two files can't be hard linked. Even if you knew all of the reasons, you don't want to have to code for all of them.

    Thanks for a great article.

    1. Zenith says:

      One reason that you would want to know if files can be hard linked without actually trying to do it is a case where you have a collection of files to hard link and don't want to be left in an aborted state when something fails in the middle. Not thinking defensively like this is why so much software is practically unusable and God help you if you ever have a problem that support's only answer, if they answer at all, in a reboot/reinstall.

      1. DWalker07 says:

        If you have a bunch of files to hard link, you can check all of them, and STILL some of the attempts to hard-link the files can fail.

        I am not sure you read the article! Checking certain conditions only tells you the "recent" state of things; File.Exists means "File existed recently", not "File Exists Now".

        You STILL may have to check for error codes, and as I mentioned, do you know all of the current and FUTURE reasons why hard-linking two files might fail?

        1. Medinoc says:

          It still reduces the window for failure in a state the program(mer) doesn't know how to recover.

  2. torrinj says:

    Ah yes, in python and probably other languages this is referred to as EAFP (Easier to ask forgiveness that permission) vs LBYL (look before you leap).

  3. Stefan Kanthak says:

    So let's try to write a shell extension for Windows^WFile Explorer to create hardlinks, for example via extending the drag&drop context menu of the right mouse button with an entry "Create hardlink(s) here".
    According to the well-known UX guidelines (context) menu entries which can't be executed should not be shown to the user.
    If the shell extension always presents "Create hardlink(s) here" due to "try to hard-link them and see if it works" this would violate the UX guidelines.
    So: how should a proper implemented shell extension handle it?

    1. Ben Voigt says:

      Well, it's because Raymond has misstated the task. The one in the post really is "Hardlink two files, if possible". The UX one really is "Determine whether two files can be hard-linked" and as you note, using the create-hardlink action to test this has undesirable side effects in the case when the answer is "it is possible".

      1. DWalker07 says:

        The task to "determine whether two files can be hard linked" should be left to the file system, not user code. There might not be a good way to test this.

        And yes, this is confusing because a hardlink is not really between two existing files....

        1. 640k says:

          Then you risk running into the user hostile UX that are all too common:
          1. Do you really want to perform action X?
          yes
          2. Haha, you're not allowed to do that!

    2. ja says:

      It is a guideline, not all options can follow this guideline "menu entries which can’t be executed should not be shown to the user". For example an Open File Menu. Even if the program check the extension to ensure the file is the corrected type, it doesn't mean the file can be opened, it might be corrupted, has wrong format with the same extension, heck it might even be on a network drive that was unplugged after the Open File Dialog was launched.

      In fact even with hard-linking, suppose you wrote a super hardlink checker that has a 100% success rate to check if it can be hard linked. That still doesn't stop the target file from being moved after running the super hardlink checker, which means your hardlink operation still fail when the user trigger it.

      In my opinion, I think the guideline were meant to display operations that are allowed in the UI for a given state and hidden when not permissible in the program state. I don't think it was meant for operations that are allowed but might possibly fail.

    3. djhayman says:

      That context menu will still show "Copy here", "More here", and "Create shortcuts here", even if you do not have write permission in the target folder, or if the target volume is full. It will also show "Move Here" even if you don't have permission to delete the original file. It will attempt to execute each action, knowing full well that it might fail.

      So in this case, it's less "don't show context menu items that can't be executed right now" and more "don't show context menu items that can never be executed".

      For example, if you have 7-Zip installed, a right-click-and-drag on a folder will add "Add to archive", while a right-click-and-drag on a file will add "Add to archive" and "Extract files here". It doesn't add "Extract files here" when you right-click-and-drag a folder, because that can never possibly work. But either way, "Add to archive" might still fail at any time because you might not have write permission to create a new file in the target folder.

    4. GL says:

      It is okay to fail a context drag & drop menu because Explorer often fails one. For example, if you are unelevated and you go to C:\Windows, hold the context button, drag and drop write.exe in-place and click Create shortcuts here, you are notified that Windows cannot create a shortcut here (because the permission was denied).

      1. Stefan Kanthak says:

        This is not OK, but a violation of one of the basic UX principles https://msdn.microsoft.com/en-us/library/dn742392.aspx#guidelines remove rather than disable context menu items that don't apply to the current context.

        1. ChDF T says:

          I think "not applicable in this context" and "not possible to execute" have two different meanings in this context.
          Consider the "execute as Administrator" context menu item. It doesn't make sense to show it on anything that isn't executable (like a .txt file), because it is not applicable in that context. However it is relevant for all executable files (like .exe files), even if the user does not have the required permissions to actually start an elevated process.
          This also exposes valuable information to the user, in case they want to start an elevated process but can't, because they don't have the required permissions.

          Additionally, it is important to keep the UI consistent. Even if you can check the feature for availability on NTFS, FAT16, FAT32 and all other file systems you know, you can't reliably do that on a file system you don't know (for example because it wasn't invented at the time of writing). So I would argue, that unless there is a documented way to check feature availability in all cases, you shouldn't check at all and deal with the consequences.

          1. Stefan Kanthak says:

            One meaning (or interpretation) of "dont apply in the current context" is "to know (or evaluate) the preconditions for an operation to succeed".
            Try to copy a file to a "shell folder" like "printers"...
            Open an elevated command prompt and try to drag&drop a filesystem object from an unelevated Explorer window into it...

            A context menu entry "Open as administrator" for text files like C:\Windows\*.ini or C:\Windows\System32\Drivers\etc\hosts would of course "make sense", if you intend to actually write changes back to these files.

            To display the context menu entry "Execute as administrator" is but nonsense if
            * the user can't get elevated rights, for example when the administrator set
            [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System]
            "ConsentPromptBehaviorUser"=dword:0
            * when the current user is already administrator^Welevated,
            * when the application does not support it (try to run Explorer.exe elevated!)
            JFTR: Windows property system allows to enable/disable menu items depending on the current context; set
            [HKEY_CLASSES_ROOT\exefile\Shell\RunAs]
            "AppliesTo"="System.FileName:<>Explorer.Exe"
            [HKEY_CLASSES_ROOT\exefile\Shell\RunAsUser]
            "AppliesTo"="System.FileName:<>Explorer.Exe"

            For consistent behaviour see the first paragraph.
            Regarding consistent behaviour and filesystems: see https://msdn.microsoft.com/en-us/library/aa364993.aspx for the documented way to check whether a filesystem supports features like hardlinks, reparse points etc.

  4. Peter says:

    I agree with the "deal with the consequences" approach in general, but MSDN makes this quite difficult. The documentation for CreateHardLink says nothing about the possible error codes for this function--just the usual "call GetLastError for more info." So there's no way I can detect and react to lack of hard link support without taking a dependency on Windows' internal implementation details.

    1. Stefan Kanthak says:

      Expect ERROR_INVALID_FUNCTION if the volume does not support hardlinks and ERROR_NOT_SAME_DEVICE if you try to create a hardlink on another volume.

      1. Joshua says:

        Correct. To find out what it was I had to probe for it. My interpretation of the resulting text was "that system call doesn't exist". Nevertheless, that's what it is.

    2. Brian_EE says:

      It seems in the instant case that the BOOL return value is enough. Either the hardlink created or it didn't. Not sure what different actions you would take on failure.

      1. skSdnW says:

        Elevate on access denied.

  5. waleri says:

    I'm just curious how do you hard link two files while they still exists?
    I was under the impression, that you'll have to delete one of them and make it point to the other one.
    At this point realizing that you cannot hard-link them might be a nasty surprise.
    Yes, I realize you still may run into a problem but you can work around that but this can be time-consuming, so it would be better to try this only if one already knows that it would worth it.

    1. cheong00 says:

      You create another hardlink pointed to the first file in "second file's folder".

      If it can be created, you could create hardlink with second file name on that folder after you deleted it. (Of course, this assume you have right to delete it)

  6. Customer's problem be damned, this is still an excellent piece on nitty-gritty ways of working with hardlinks.

  7. santoshsa says:

    What happens in this somewhat related case?
    Using remote desktop from a client which has C: and subst V: pointing to C:\folder.
    Inside session we map drives. For e.g. net use Z: \\tsclient\C and net use Y: \\tsclient\Y

    If an app inside remote desktop accesses Z:\folder\test.txt and Y:\test.txt which are same file really.
    Copying file via explorer from above src to destination throws access denied because of incompatible file share access.
    But I imagine an application which doesn't know both are referring to same file (if it opens with file_shared_read|file_shared_write) corrupting the file(s).

Comments are closed.

Skip to main content