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
- Use a tool that will show you the hardlink-peers.
- Use a tool that will show you processes with a handle that is open to the hardlink or its peers. Manually close/kill them.
- 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).
- 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:
- Local to local symbolic links, L2L:{0|1}
- Local to remote symbolic links, L2R:{1|0}
- Remote to local symbolic links, R2L:{1|0}
- Remote to remote symbolic links, R2R:{1|0}
cmd /c del hard_linked_file_to_be_removed.exe
. Note how you have to typecmd
from withinCMD
!