16

On a Windows NTFS file system, I have a file (say, orig.mp3). I open this file, through this path orig.mp3, in such a way that it is in use (say, by playing it in VLC).

Then I create a hard link (cmd /c mklink /h link.mp3 orig.mp3). This results in two NTFS paths pointing to exactly the same file.

Finally I try to delete linked file again (del link.mp3, or delete in Windows Explorer).

This fails with an error: "The process cannot access the file because it is being used by another process."

Why? And more importantly: how can I avoid this (apart from making sure no process has the original file in use)? Can I perhaps tell Windows to do a 'delayed delete', so that the linked file is automatically deleted when the original is no longer in use?

2
  • 2
    Should probably be asked on superuser. But still: movefile should be able to do the trick in any case. It's surprising that you cannot simply delete the hard link though, actually it should be a completely "unrelated" file.
    – Damon
    Nov 20, 2013 at 12:30
  • As the error says, make sure you are not running anything that may use the file. (In your case it is VLC and even if you close VLC, it's possible that Windows have not yet released the process from memory (to keep a new start-up quick). Also make sure to close any shells where you may have accessed it from before. Then open a new CMD as Admin and type: cmd /c del hard_linked_file_to_be_removed.exe. Note how you have to type cmd from within CMD!
    – not2qubit
    Oct 5, 2020 at 0:49

5 Answers 5

12

This is quite expected behavior, the hard link is just another name for the same file. E.g., if you have file A.PDF, create hard link B.PDF to the same file, it doesn't matter whether the file is opened under the name A.PDF or B.PDF - it's still the same file, so if this file is simply opened, you can't delete either link.

The actual reason is that the name is stored as an attribute in the file record of master file table (in case of NTFS) and since the file is opened, you can't delete either link (you can't modify opened file).

In this case there's nothing like original file, since both names belong to the same (and the only one) file and both names are equal. The file is actually deleted when link count reaches zero.

3
  • Thanks for this answer to "Why?" As you can see, I've added my own answer on "How to avoid?" Nov 20, 2013 at 15:53
  • 7
    The "why" is incomplete; if deleting a hardlink is a modification, then so is adding one, yet you can add hardlinks to open files, just not delete or rename them. I think the "why" is just that it was decided that hardlinks can't be renamed or deleted while a file is open; an intentional design decision.
    – RomanSt
    Oct 28, 2014 at 13:19
  • 3
    Actual reason is that, typically, whatever has the other hardlinks OPEN has not called FILE_SHARE_DELETE. Therefore DELETE permission FAILs during DeleteFile call. See notes in: docs.microsoft.com/en-us/windows/win32/api/fileapi/… Mar 15, 2021 at 22:16
5

While the above answers are not exactly wrong about WHEN and WHY you can or cannot delete a hardlink the more precise reason can be found looking at this link:

FILE_SHARE_DELETE link in the CreateFileA documentation.

If this flag is not specified, but the file or device has been opened for delete access, the function fails.

A CreateFile call will fail when requesting DELETE dwDesiredAccess if any handles are open to that file (including its hardlink-peers) where those handles were NOT opened with FILE_SHARE_DELETE permission.

That is the general reason why attempting to delete a hardlink may fail (error code 5 access violation); requiring that all open hardlink-peers of that file must be closed in order to delete the hardlink.

It is a common pattern to create/delete hardlinks to executables (perhaps nodejs/npm) for nix-busybox style EXE name-aliasing to share a single EXE binary.

For executables that are hardlinked, deleting a hardlink to an executable which has a hardlink-peer running WILL FAIL because the Windows loader DOES NOT open executable files with FILE_SHARE_DELETE permission. Which makes managing hardlinks via deletion for install remove/updating quite annoying when service-exes are running where you used shared-exe hardlink-peer naming to create those service-exes . As a pattern, use symlinks instead if that is your issue. Although, unlike with hardlinks, your service-exes in any process-viewer (or similar process enumeration api) will use the symlinks target-name not the symlink name [sigh]).

It is correct that hardlinks share an NTFS file description.

i.e., same structure on volume. Also note that hardlinks share the same NTFS FILE_ID_INFO see GetFileInformationByHandleEx - FileIdInfo (0x12).

But that is NOT the reason you can't delete a hardlink while a peer-link is open. This is evidenced further by the fact that EVEN when a peer-link is open you CAN create more hardlinks to ANY of the peer hardlinks; even though you can't delete them while any peer is open without FILE_SHARE_DELETE permission.

How you might go about deleting a hardlink with a peer-link open

  1. Use a tool that will show you the hardlink-peers.
  2. Use a tool that will show you processes with a handle that is open to the hardlink or its peers. Manually close/kill them.
  3. Use a tool that is a file-watcher or use file-watcher apis to delete the hardlink when all the hardlink-peers are closed (via #2).
  4. Use the above mentioned Win32 MoveFileEx function with the MOVEFILE_DELAY_UNTIL_REBOOT and wait/require a reboot to delete the hardlink.

For reference, I have a toolset that uses these type of hardlink patterns. It can perform all the above (it is a general shell and dynamic language ess). The toolset is EdgeShell with a single no-install binary named afm.exe.

API Note: to do the same actions as DeleteFile the dwFlagsAndAttributes need only be FILE_FLAG_DELETE_ON_CLOSE and FILE_FLAG_OPEN_REPARSE_POINT (to properly handle symlink and junction reparse points rather than their targets).

API Note: you can enumerate all the hardlink peers using FindFirstFileNameW, FindNextFileNameW, and FindClose. A hardlink-peer is just another NTFS directory node entry name reference (pathname) to an NTFS file node. Since they share the NTFS volume's file-itself and hardlink-xref (backpointers) to dir-nodes information is kept in that file-node, hardlinks have to be on the same NTFS volume.

Note: Copying to a hardlinked-file and NTFS file-sharing of reparse-points

It is probably VERY unintuitive that if you use most command line or editor tools to COPY a file onto a hardlink, it usually will DELETE/BREAK the hardlink and create a NEW standalone copy of that file.

If your goal was to actually replace the contents of the hardlink file so that the hardlink and ALL its peers would share the copied data then you typically need a tool (w/wo flag options) that is hardlink aware for that action.

It is best, if you don't have such tools, to "open" the hardlink file in question and "write" the desired new content in place (do not use a generic file-copy tool). Be aware that many IDE editors (VsCode, Visual Studio) will use std c-lib apis that actually delete then replace the file (breaking your hardlink). Which can get very confusing.

The Windows world has always had great NTFS capabilities but has long lacked nix knowledge and tools for handling hardlinks, symlinks (and junctions).

FILE SHARES and LINKS: In point of fact, JUNCTIONS are awesome. Especially when you understand the real difference between how JUNCTIONS and SYMLINK reparse points are treated in a network file-share of an NTFS volume.

I.e., a SYMLINK is resolved on the CLIENT machine, but a JUNCTION is resolved on the SERVER machine.

Which means that an NTFS volume with a symlink to say C:\something will look on the C drive of the client machine (because the link is resolved on the client), but a JUNCTION will look on the C drive of the server (because the link is resolved on the server) and thus resolves path on server and doesn't expose the junction-reparse to the client.

For configuring NTFS share behavior see

fsutil behavior set SymlinkEvaluation

symlinkevaluation <symboliclinktype> Controls the kind of symbolic links that can be created on a computer. Valid choices are:

  1. Local to local symbolic links, L2L:{0|1}
  2. Local to remote symbolic links, L2R:{1|0}
  3. Remote to local symbolic links, R2L:{1|0}
  4. Remote to remote symbolic links, R2R:{1|0}
3

As detailed in Robert Goldwein's answer, such a hard link cannot be deleted while the file is in use. However, a delayed delete turns out to be possible.

Damon's comment on this question suggests to use movefile from the Sysinternals Suite.

In my case, where I want to do this from PowerShell, I can use Lee Holmes's Move-LockedFilelink.mp3 $null, to have Windows delete the file at the next boot.

Both of the above use the Win32 MoveFileEx function with the MOVEFILE_DELAY_UNTIL_REBOOT flag.

Update: See https://gist.github.com/marnix/7565364 for a Remove-File-Eventually which I just hacked up. No guarantees. :-)

3

Use the FSUTIL tool to maintain the symlinks safely.

http://technet.microsoft.com/en-ca/library/cc753059.aspx

fsutil reparsepoint delete link.mp3

would remove the hardlink while preserving orig.mp3

5
  • 2
    But would this work on hard links? I don’t believe so.
    – Daniel B
    Oct 30, 2014 at 17:03
  • 1
    Well... I got this. Error: The process cannot access the file because it is being used by another process.
    – ST3
    Oct 2, 2015 at 13:58
  • fail. same as @ST3. Sep 18, 2019 at 20:14
  • This worked for me for a directory hard link (directory->directory). Kept original files intact.
    – Jon
    Jul 16, 2020 at 15:24
  • Didn't work for me, for a real file...
    – not2qubit
    Oct 4, 2020 at 23:13
-1

mklink is a CMD internal command and does not work on a FAT FS. To remove a hardlink, you need to exit anything that may use it, and that include anything that is ran by Powershell. Exit everything and start a new CMD as Admin, then use fsutil: cmd /c fsutil hardlink list <hardlinked_filename/dir>, to ensure what kind of link it is. (Note how this is not available directly from W10 CMD prompt.) You may also try or cmd /c dir /A:S.

To remove, either use del with cmd /c del hardlinked_file.exe (from within a CMD shell) or go into WSL and try to remove it from there, as it is (probably) better at handling symlink removals. If that doesn't work, use the sysinternals tools; movefile64.exe and pendmoves64.exe to check.

As a last resort, you may want to install HardLinkShellExt to make, adjust and delete all sorts of symbolic links. However, this is an advanced tool that is hard to understand without considerable techno-reading.

2

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .