2

I've been learning about NTFS links (1, 2) and playing around with them on my computer. It's a strange world of pseudonyms for filenames and folder names, and I'm not clear yet on why I have them, but what is certain is that I have lots of them.

NTFS links are either hard links or reparse points and reparse points are either junction points or symbolic links.

In order to become more familiar with them, I've been attempting to generate a complete list of all the NTFS links on my computer.

This is a dual-drive computer with the OS on C: and data on D:. The OS is Windows 10 Pro 64 v 1903 (I'm reporting here on results logged before the update to v 1909). PowerShell is the default Windows v 5.1.

In what follows, the terms "directory" and "folder" are synonymous.

All links

PowerShell apparently has, since v 5.0, two undocumented properties of "item" cmdlets: LinkType and target (1, 2, 3, 4). LinkType has the values "Junction", "SymbolicLink", and "HardLink". So this should be able to list all the NTFS links on my computer. However, it does not work reliably. In particular, it fails on certain objects in the "Users" folder. For example, in PowerShell:

PS C:\WINDOWS\system32> echo ("1. " + ("C:\Documents and Settings" | get-item -force).LinkType)
echo ("2. " + ("C:\Program Files\Microsoft Office\root\Client\AppvIsvSubsystems32.dll" | get-item -force).LinkType)
echo ("3. " + ("C:\Program Files\NVIDIA Corporation\NvTelemetry\plugins\NvTelemetry" | get-item -force).LinkType)
echo ("4. " + ("C:\ProgramData\Desktop" | get-item -force).LinkType)
echo ("5. " + ("C:\Users\All Users" | get-item -force).LinkType)
echo ("6. " + ("C:\Users\Default User" | get-item -force).LinkType)
1. 
2. SymbolicLink
3. Junction
4. 
5. 
6. 

The corresponding results from dir /aL in the Windows Command Prompt (see next below) are:

1. JUNCTION
2. SYMLINK
3. JUNCTION
4. JUNCTION
5. SYMLINKD
6. JUNCTION

So it appears that, at least for PowerShell 5.1, LinkType cannot be trusted.

Reparse points

In the PowerShell cmdlet get-ChildItem, the parameter attribute has the property ReparsePoint. This should allow identifying reparse points, but does not distinguish between junction points and symbolic links, so it is not as useful as dir /aL, discussed next.

The Windows (Command Prompt, not PowerShell) command dir /aL /s X:\ lists all the reparse points in directory X. Running as administrator, it found none on the data drive and 574 on the system drive, mostly in the folders "Program Files" (not "Program Files (x86)") and "Users", and also a few in "Program Data" and one in "Windows".

In the output of that dir command, targets are indicated in square brackets after the object names and the column that usually has the file size or "<DIR>" in it now has five different values: 0 (which is presumably a file size), the usual <DIR>, and the three new values: <JUNCTION>, <SYMLINK>, <SYMLINKD>. On my computer, on the system drive only, these values occurred with the following frequency and characteristics of the link and target objects:

 Count   Type/Size    Link object        Target object
   11    <JUNCTION>   Folder with root   Folder with root
   36                 Folder not found   Folder with root
    9                 Folder not found   Folder w/o root
    7                 Folder not found   Folder not found
   10    <SYMLINK>    dll file           dll file
    1    <SYMLINKD>   Folder w/o root    Folder w/o root
  488    <DIR>        Folder with root   None
   12    0            exe file           None
 ----
  574    Total

In that table, the object types have the following meanings: (In this item list, dir means running dir on the object in the Command Prompt (not PowerShell) as administrator with no parameters (specifically, without the /aL parameter).)

  • Folder with root: dir yields a listing that looks like a regular directory listing, starting with <DIR> . to represent the object (directory) itself.
    • Example (target object): dir "C:\Users\Public\Documents"
  • Folder w/o root: dir yields a listing of one or more objects that does not start with either <DIR> . or the name of the object itself.
    • Example (target object): dir "C:\Users\Public\Desktop"
  • Folder not found: dir yields "File not found" and the name of the object does not look like a filename with an extension. (In PowerShell, dir yields "Access to ... is denied.")
    • Example (link object): dir "C:\Documents and Settings"
  • File: dir yields a listing of one item, which is the name of the object itself.
    • Example (link object): dir "C:\Program Files\Microsoft Office\root\Office16\C2R64.dll"

Obviously, <JUNCTION> indicates a junction point, while <SYMLINK> and <SYMLINKD> indicate symbolic links to files and folders. But I have questions about other information here:

  • What are the 500 objects that dir /aL says are reparse points, but are marked as <DIR> or zero file size, and with no targets? Are the <DIR> objects junction points or symbolic links or something else? Are the zero-size files symbolic links or something else? If they are links, what are they links to?
  • What are the directory listings that do not start with <DIR> . ("Folder w/o root")? I've never seen that before.
  • Why are some junction points and their targets found by dir (without /aL), while others are not?

Hard links

There does not seem to be an easy, native way to get a listing of hard links. Here are six Stack Exchange answers I've found so far on the subject:

I have not finished testing all the methods. Any suggestions for pursuing this line of inquiry effectively to get a correct list of all hard links?

Summary

  • Re all links: Any comment on my finding that LinkType cannot be trusted to report all NTFS links correctly?
  • Re reparse point: Any answers to the three questions stated at the end of that section?
  • Re hard links: Any suggestion for getting a good listing?
  • Any other wisdom or insights to share about this strange world of file system pseudonyms?

1 Answer 1

3

Re hard links: Any suggestion for getting a good listing?

I doubt hard links will ever show up as a distinct LinkType once they're created, because the way they work is by having multiple names (directory entries) point to the same file object the same way as the original name does.

The only way to determine whether a file has hardlinks is to check its "link count", as in the fsutil script you've found. Similarly on Linux, POSIX stat() has the 'nlink' attribute telling you the number of links a file has (this is the number that shows up in ls -l).

However, there is very little in NTFS – and nothing in most Unix filesystems – that would allow you to distinguish a hardlink from the original. You can only tell two files are hard-linked because they point to the same 'inode' (well, the NTFS equivalent of), but you will not know which link was added later: it's simply a file with two names.

NTFS links are either hard links or reparse points and reparse points are either junction points or symbolic links

[...]

What are the 500 objects that dir /aL says are reparse points, but are marked as or zero file size, and with no targets? Are the <DIR> objects junction points or symbolic links or something else? Are the zero-size files symbolic links or something else? If they are links, what are they links to?

Not all reparse points are links. They can have various tags in them, and their general purpose is just to redirect the file lookup to some driver.

  • For example, Windows allows drives/volumes to be mounted on an empty folder (Unix style) instead of giving them a "drive letter". Unlike Unix mountpoints which are transient, however, Windows mountpoints are persistent in the filesystem – they're a type of reparse point that stores the mounted volume ID.

  • Another very common use of reparse points is to implement "cloud" or "online-only" files, as in OneDrive and Dropbox (both of which implement it very differently too) – they appear as regular files that just trigger an online download once opened.

If you're using Microsoft Office installed in a certain way (not entirely sure, but I think it's Office 365 installed through ClickOnce) it seems to use yet another type of reparse points, which again are neither junctions nor symlinks. Use fsutil reparsepoint query to see if you can find anything interesting.

Folder not found: dir yields "File not found" and the name of the object does not look like a filename with an extension. (In PowerShell, dir yields "Access to ... is denied.")

Example (link object): dir "C:\Documents and Settings"

"File not found" is unfortunately how CMD reports errors caused due to security restrictions as well – it doesn't mean it successfully retrieved an empty list.

"Documents and Settings" is a standard junction point, it just has an ACL (see icacls) which explicitly denies listing its contents. There was an old blogs.msdn.com post saying that this was done to avoid certain link-ignorant programs scanning the same user profile directories twice.

2
  • To clarify, a filesystem mountpoint in Windows is a junction. It's just another word for the same thing, i.e. an IO_REPARSE_TAG_MOUNT_POINT reparse point. The semantics of junctions are implemented to behave like a Unix mountpoint, and it's closest to what Unix calls a bind mount.
    – Eryk Sun
    Oct 23, 2020 at 22:07
  • 1
    Unlike a symlink, a junction is not replaced by its target in the opened path while reparsing a relative symlink in the kernel, so it gets traversed like a regular ancestor directory by ".." components. For example, "C:\spam\junction\relative_symlink", where "relative_symlink" -> "..\eggs", ultimately resolves to "C:\spam\eggs". If "junction" were replaced by an "absolute_symlink" that targets "Z:\foo" and "Z:\foo\relative_symlink" is the same relative target "..\eggs", then "C:\spam\absolute_symlink\relative_symlink" would resolve to "Z:\eggs".
    – Eryk Sun
    Oct 23, 2020 at 22:07

You must log in to answer this question.

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