Here's some odd behavior I encountered with
mklink which ships in Windows since Vista. I suspect if may be a defect in
mklink or even as deep as the NTFS file system driver, but the behavior could use some explanation. This behavior was encountered on Windows 7 and 10 respectively.
Assume we have a directory on an NTFS volume (do NOT try this on anything but a volume you're creating for this sole purpose!) and a file named
bar.txt inside of it.
md F:\1 echo foo > F:\1\bar.txt
Now issue the following command (via privileged prompt):
mklink F:\1:bar F:\1\bar.txt
... which should give you:
symbolic link created for F:\1:bar <<===>> F:\1\bar.txt
Don't worry, I know this is silly. But it was the result of a test whether an alternate data stream (ADS) could become a reparse point. I held that it couldn't, because an alternate data stream only has a name, a size and - well - the data inside of it. Unlike a file or a directory it doesn't have file attributes or own time stamps, and so there'd be no attribute to designate the ADS as reparse point (which otherwise happens through the file attributes). Or differently put: reparse points can only refer to directory entries (via
$Extend\$Reparse), whereas ADS are tied to directory entries.
The outcome of the above command is this:
F:\>dir /r Volume in drive F is TEST Volume Serial Number is 24F3-8A7D Directory of F:\ 2018-04-03 20:47 <SYMLINKD> 1 [F:\1\bar.txt] 0 1:bar:$DATA 0 File(s) 0 bytes 1 Dir(s) 4,244,283,392 bytes free
Unsurprisingly attempting to change into this directory does not work and yields
The directory name is invalid.
Similarly attempting to delete the reparse point by either using
junction -d (from the Sysinternals Suite) or using
fsutil reparsepoint delete fails with the very same error. Only inspecting the reparse point data gives me something to cling on to:
F:\>fsutil reparsepoint query F:\1 Reparse Tag Value : 0xa000000c Tag value: Microsoft Tag value: Name Surrogate Tag value: Symbolic Link Reparse Data Length: 0x00000044 Reparse Data: 0000: 18 00 20 00 00 00 18 00 00 00 00 00 46 00 3a 00 .. .........F.:. 0010: 5c 00 31 00 5c 00 62 00 61 00 72 00 2e 00 74 00 \.1.\.b.a.r...t. 0020: 78 00 74 00 5c 00 3f 00 3f 00 5c 00 46 00 3a 00 x.t.\.?.?.\.F.:. 0030: 5c 00 31 00 5c 00 62 00 61 00 72 00 2e 00 74 00 \.1.\.b.a.r...t. 0040: 78 00 74 00 x.t.
Now my question is what happened here and how do I get rid of such a reparse point again with on board Windows tools (or, failing that, external ones)? Bonus points for being able to answer what happened to the file inside folder
1 and disclosing your methodology.
My working theory so far is as follows:
mklinkcreates the "file"
F:\1:barand succeeds (presumably via
REPARSE_DATA_BUFFERon the created "file" which can't work as it's an ADS on a directory. So internally what happens is that the file system driver sets the reparse data buffer on the directory.
The outcome is what we see. What bothers me here is that normally you can't get a handle on a directory without specifying a particular flag. So not only have we caused
mklink to create a symbolic link on a directory to a file, we also dodged the necessity to specify
The documentation of
You must set this flag to obtain a handle to a directory. A directory handle can be passed to some functions instead of a file handle. For more information, see the Remarks section.
To reproduce I strongly advise you to not attempt this on an existing NTFS drive but instead to create a fresh one using the ImDisk RAM disk driver and the accompanying
imdisk command line tool (via privileged prompt):
imdisk -a -t vm -p "/fs:ntfs /q /y /v:TEST" -s 4G -m F:
(alter the parameters however you see fit.
-m denotes a drive letter and
-s the size of the RAM disk.)