Skip to content

Fall back to hard link when symlink creation lacks privilege#6270

Draft
rich-purnell wants to merge 2 commits into
microsoft:masterfrom
rich-purnell:fix/create-symlink
Draft

Fall back to hard link when symlink creation lacks privilege#6270
rich-purnell wants to merge 2 commits into
microsoft:masterfrom
rich-purnell:fix/create-symlink

Conversation

@rich-purnell
Copy link
Copy Markdown

@rich-purnell rich-purnell commented Jun 6, 2026

📖 Description

When WinGet runs without administrator privileges or Developer Mode enabled, std::filesystem::create_symlink() fails with the error:

Error code: 1314 - create_symlink: A required privilege is not held by the client.

This happens because creating symbolic links requires the SeCreateSymbolicLinkPrivilege, which is only granted to administrators or available when Developer Mode is turned on. While WinGet typically runs with those privileges, there are scenarios where they are not held — for example, when running as a standard user or in a restricted environment.

I wrote a local test program that confirmed this behavior. Under those conditions, std::filesystem::create_symlink(target, link) fails with the above error, but std::filesystem::create_hard_link(target, link) succeeds. Since a hard link provides identical file resolution behavior for this use case (pointing to the same file on disk), it is a viable fallback.

This change adds a fallback: when symlink creation fails specifically with ERROR_PRIVILEGE_NOT_HELD, the code attempts a hard link instead before returning false.

Such a change may not solve all problems (for example, in some special file system structures), but it can reduce the impact scope.

🔗 References

Resolves #6058

🔍 Validation

Build succeeds with no errors or warnings.

✅ Checklist

📋 Issue Type

  • Bug fix
  • Feature
  • Task
Microsoft Reviewers: Open in CodeFlow

@rich-purnell rich-purnell requested a review from a team as a code owner June 6, 2026 20:00
@rich-purnell
Copy link
Copy Markdown
Author

@microsoft-github-policy-service agree

Copy link
Copy Markdown
Contributor

@Trenly Trenly left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The uninstall flow may need to account for hard links with this change. Tests should be added to validate hardlink creation, path resolution, and removal.

What happens in the upgrade scenario where a hardlink exists but the user now has symlink permissions - does it stay a hardlink, or should it be replaced with a symlink?

@rich-purnell
Copy link
Copy Markdown
Author

rich-purnell commented Jun 6, 2026

The uninstall flow may need to account for hard links with this change. Tests should be added to validate hardlink creation, path resolution, and removal.

What happens in the upgrade scenario where a hardlink exists but the user now has symlink permissions - does it stay a hardlink, or should it be replaced with a symlink?

I see that in the current installation flow, before creating a symlink, std::filesystem::remove(filePath) is called to remove the existing symlink. My testing shows that it also correctly removes hard links. So when a user gains permissions, reinstallation will remove the hard link and then create a symlink. I did overlook the uninstall part – I will check it later. Regarding adding new tests, since this is my first time working on this project, I need some time to understand which scenarios are already covered by the existing tests.

@rich-purnell rich-purnell marked this pull request as draft June 6, 2026 20:55
@microsoft-github-policy-service microsoft-github-policy-service Bot added Needs-Attention Issue needs attention from Microsoft and removed Needs-Author-Feedback Issue needs attention from issue or PR author labels Jun 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Needs-Attention Issue needs attention from Microsoft

Projects

None yet

Development

Successfully merging this pull request may close these issues.

WinGet install with PortableCommandAlias gives wrong message when installed with non elevated privileges

2 participants