By Yarden Shafir
Did you know that symbolic links (or symlinks) created through Windows Subsystem for Linux (WSL) can’t be followed by Windows?
I recently encountered this rather frustrating issue as I’ve been using WSL for my everyday work over the last few months. No doubt others have noticed it as well, so I wanted to document it for anyone who may be seeking answers.
Let’s look at an example of the issue. I’ll use Ubuntu as my Linux client with WSL2 and create a file followed by a symlink to a file in the same directory (via ln -s
):
echo "this is a symlink test" > test_symlink.txt ln -s test_symlink.txt targetfile.txt
In WSL, I can easily read both the original file (test_symlink.txt
) and the symlink (targetfile.txt
). But when I try to open the symlink from the Windows file explorer, an error occurs:
The same error occurs when I try to access targetfile.txt
from the command line:
Looking at the directory, I can see the target file, but it has a size of 0 KB:
And when I run dir
, I can see that Windows recognizes targetfile.txt
as an NTFS junction but can’t find where the link points to, like it would for a native Windows symlink:
When I asked about this behavior on Twitter, Bill Demirkapi had an answer—the link that is created by WSL is an “LX symlink,” which isn’t recognized by Windows. That’s because symlinks on Linux are implemented differently than symlinks on Windows: on Windows, a symlink is an object, implemented and interpreted by the kernel. On Linux, a symlink is simply a file with a special flag, whose content is a path to the destination. The path doesn’t even have to be valid!
Using FileTest, we can easily verify that this is a Linux symlink, not a Windows link. If you look carefully, you can even see the path to the destination file in the file’s DataBuffer:
FileTest can also provide a more specific error message regarding the file open failure:
It turns out that trying to open this file with NtCreateFile
fails with an STATUS_IO_REPARSE_TAG_NOT_HANDLED
error, meaning that Windows recognizes this file as a reparse point but can’t identify the LX symlink tag and can’t follow it. Windows knows how to handle some parts of the Linux filesystem, as explained by Microsoft, but that doesn’t include the Linux symlink format.
If I go back to WSL, the symlink works just fine—the system can see the symlink target and open the file as expected:
It’s interesting to note that symlinks created on Windows work normally on WSL. I can create a new file in the same directory and create a symlink for it using the Windows command line (cmd.exe
):
echo "this is a test for windows symlink" > test_win_symlink.txt mklink win_targetfile.txt test_win_symlink.txt
Now Windows treats this as a regular symlink that it can identify and follow:
But the Windows symlink works just as well if we access it from within WSL:
We get the same result if we create a file junction using the Windows command line and try to open it with WSL:
echo "this is a test for windows junctions" > test_win_junction.txt mklink /J junction_targetfile.txt test_win_junction.txt
This is how the directory now looks from Windows’s point of view:
And this is how it looks from WSL’s point of view:
Hard links created by WSL do work normally on Windows, so this issue applies only to symlinks.
To summarize, Windows handles only symlinks that were created by Windows, using its standard tags, and fails to process WSL symlinks of the “LX symlink” type. However, WSL handles both types of symlinks with no issues. If you use Windows and WSL to access the same files, it’s worth paying attention to your symlinks and how they are created to avoid the same issues I ran into.
One last thing to point out is that when Bill Demirkapi tested this behavior, he noticed that Windows could follow WSL’s symlinks when they were created with a relative path but not with an absolute path. On all systems I tested, Windows couldn’t follow any symlinks created by WSL. So there is still some mystery left here to investigate.