Skip to content

Target type detection (file/directory) for relative symlinks is still broken, even on Windows #15235

@MatejKafka

Description

@MatejKafka

#12797 (comment)

Intro & context

Symlink target may be a relative path, which is then resolved by the filesystem as a relative path from parent directory of the symlink. Example:
We have the following 2 files:

    Directory: D:\test

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
la---          2021-04-15    00:41              0 file_link.txt -> .\file.txt
-a---          2021-04-15    00:41              0 file.txt

file_link.txt is a file symlink, which resolves to D:\test\file.txt. It might be created with New-Item -Type SymbolicLink .\file_link.txt -Target .\file.txt.

There's a difference between a symlink to a file and to a directory, and attempting to traverse a directory symlink pointing to a file does not work (and same for trying to read from a file symlink pointing to a directory).

To correctly create the symlink, PowerShell checks whether the target is a file or a directory. Originally, this check was broken for relative target paths, because the process-wide .NET working directory was used to resolve the target (so the above command would check for a target path like C:\Windows\System32\file.txt - that path doesn't exist, so pwsh defaults to creating a file symlink). This was supposedly fixed in #12797.

With this "fix", working directory of PowerShell is now used instead of the .NET working directory. This is however still incorrect, because we don't want to resolve the target relative to CWD, but relative to the symlink itself.

Reproduction

(I'll use D:/test as our testing directory to make it clear when I'm talking about absolute paths)

1)

mkdir D:/test
mkdir D:/test/dir
cd D:/
ni -Type SymbolicLink D:/test/dir_link -Target ./dir

Expected: directory symlink to D:/test/dir, with target ./dir
Got: file symlink to D:/test/dir, with target ./dir
The target type detection code checks the relative path ./dir (D:/dir), which doesn't exist, so the symlink incorrectly defaults to file symlink.

2)

mkdir D:/test
mkdir D:/test/dir
cd D:/test
ni -Type SymbolicLink D:/test/dir_link -Target ./dir

Expected: directory symlink to D:/test/dir, with target ./dir
Got: what I expected
Only difference from previous example is the cd D:/test on line 3. This correctly creates a directory symlink, because the code checks type of target for ./dir (D:/test/dir), which now matches the real target.

3)

mkdir D:/test/subdir
mkdir D:/test/item # <-- directory
ni D:/test/subdir/item # <-- file
cd D:/test
ni -Type SymbolicLink D:/test/subdir/item_link -Target ./item

Expected: file symlink to D:/test/subdir/item, with target ./item
Got: directory symlink D:/test/subdir/item, with target ./item
The the file D:/test/item is used for target check, it's a directory, so a directory symlink is created (but the actual target is D:/test/subdir/item, which is a file).

Proposed fixes

There are 2 possible correct behaviors I see here:

  1. Always resolve symlink target to an absolute path in New-Item. This effectively prevents PowerShell from creating symlinks with relative paths to target.
  2. Allow relative paths, but when checking for target type (file/directory), use the path Join-Path $Path $Target internally, but still write $Target as provided by user (relative) into the symlink itself. This is imo the better solution, allows the user to create symlinks with relative paths, but still correctly detects the type.

Environment data


Name                           Value
----                           -----
PSVersion                      7.2.0-preview.4
PSEdition                      Core
GitCommitId                    7.2.0-preview.4
OS                             Microsoft Windows 10.0.19042
Platform                       Win32NT
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0…}
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1
WSManStackVersion              3.0

Metadata

Metadata

Assignees

No one assigned

    Labels

    Area-FileSystem-Providerspecific to the FileSystem providerIn-PRIndicates that a PR is out for the issueIssue-BugIssue has been identified as a bug in the productWG-Cmdlets-Managementcmdlets in the Microsoft.PowerShell.Management moduleWG-Engine-Providersbuilt-in PowerShell providers such as FileSystem, Certificates, Registry, etc.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions