How to delete Windows NTFS hard link (mklink /h) while original is in use?
2014-04
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?
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.
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-LockedFile
link.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. :-)
I foolishly used Dupemerge to change all my duplicate files into hard links. Now Windows XP is not running right, eg, explorer won't start.
Is there a utility which would traverse the filesystem looking for hard links, copy the file, delete the original link, and rename the copy, keeping the original attributes and name?
I doubt that there's a utility for undoing what was done. You can search for duplicates again, check their link counts and attributes (or maybe Dupemerge can help identify hard links to the same files) and do the copying by hand. This may at least help you find out whether hard links are the cause of problems.
Since you've converted them into hard links, you might be in luck and they might still show up as duplicates using something like DoubleKiller.
Either way, I doubt there's a utility for this exact task.
If all else fails I recommend a re-install...
to fix the operating system use the system file checker:
insert the windows xp installation CD
press CTRL + ALT + DEL to bring up the task manager, go to File > Run (New Task) and type sfc /scannow and click OK.
note: this will only restore the system files, but it will get you going again. as for other software affected you'll have to re-install or repair install where necessary.
SameFiles Assistant 3.1 might work:
Same Files Assistant is the hard links managing utility.
Specifically one feature it has:
- You can roll back hard links to the regular files at any time.
Try Hard Link Magic, it might help.
Also Microsoft's Junction has the ability to recursively traverse directories and list/delete junction-points.
Just be careful to create a system restore point before you do these manipulations.
I have written a Perl script that identifies all regular files that are hard links to the same data. The script works fine on UNIX and Cygwin. I haven’t tested it with Strawberry Perl or any other Windows port of Perl, but I thought I’d share it anyhow. On Windows (Cygwin) I would open a terminal and do ./list-dup-hard-links /cygdrive/c/
.
#!/usr/bin/perl
#
# NAME
#
# list-dup-hard-links - list regular file names pointing to the same inode
#
# SYNOPSIS
#
# list-dup-hard-links DIRECTORY
#
# DESCRIPTION
#
# For each inode that is referred to by more than one regular file, print
# the inode number and the list of corresponding files.
#
# AUTHOR
#
# Peter John Acklam <[email protected]>
use strict; # restrict unsafe constructs
use warnings; # control optional warnings
use File::Find; # traverse a file tree
if (@ARGV != 1) {
die "Usage: $0 DIRECTORY\n";
}
my $start_dir = shift; # starting directory
my $start_dev = (stat $start_dir)[0]; # device number of where we start
my %inum2files; # map each inode number to file(s)
sub wanted {
return if -l; # skip symlinks
my @fileinfo = stat(); # get file info
if (-d _) { # if a directory
my $this_dev = $fileinfo[0]; # get device number
if ($this_dev != $start_dev) { # if we crossed a device boundary
$File::Find::prune = 1; # mark directory for pruning
return; # and return
}
}
return unless -f _; # continue only if a regular file
my $inum = $fileinfo[1]; # get inode number
push @{ $inum2files{$inum} }, # append this file to the list of
$File::Find::name; # all files with this inode number
}
find(\&wanted, $start_dir); # traverse the file tree
while (my ($inum, $files) = each %inum2files) {
next if @$files < 2; # skip non-duplicates
print "\nInode number: $inum\n\n" # print header
or die "$0: print failed: $!\n";
for my $file (@$files) {
print " $file\n" # print file name
or die "$0: print failed: $!\n";
}
}
In Unix, hard links to one file links same "inode number". "stat" function returns file properties like size, mode, alter date, modification date, inode number, ..., but return inode number "0" for any file in Windows. Use perl Win32::IdentifyFile (CPAN) to get a file disk "localization". Hard links "links" to same disk "localization".