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:
mklink
creates the "file" F:\1:bar
and succeeds (presumably via CreateFile()
).mklink
sets the REPARSE_DATA_BUFFER
on 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 FILE_FLAG_BACKUP_SEMANTICS
.
The documentation of FILE_FLAG_BACKUP_SEMANTICS
under CreateFile
reads:
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.)
User contributions licensed under CC BY-SA 3.0