Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a page about symlinks #337

Merged
merged 2 commits into from
Aug 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ nav:
- docs/package-management-tips.md
- docs/package-management-faq.md
- docs/filesystem-paths.md
- docs/symlinks.md
- docs/configuration.md
- Languages & Tools:
- docs/cmake.md
Expand Down
110 changes: 110 additions & 0 deletions web/docs/symlinks.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
# Symlinks

[Symlinks](https://en.wikipedia.org/wiki/Symbolic_link) are a way to create a
reference to a file or directory in another location. They are quite common on
Linux/Unix systems, and many Unix tools are using/depending on them. While
symlinks also exist on Windows, they are not available to normal Windows users
by default, which makes them less common on Windows.

In MSYS2 symlinks are by default avoided, and creating a symlink with Cygwin
tools instead creates a deep-copy of the target. This makes sure that symlinking
works on every Windows installation and for every Windows user and the result is
readable by native Windows software.

The deep-copying has the downside that the symlink and the target are not
actually linked, and due to the duplication it also takes up double the disk
space. Another downside is that the target always needs to exist, otherwise it
couldn't be copied.

We are planning to improve this in the future, see
https://github.com/msys2/msys2-runtime/pull/114 for more information.

## Cygwin Symlink Modes

While MSYS2 is based on Cygwin, Cygwin has different default behavior for
symlinks and tries to create "real" symlinks if possible, with the potential
downside that they are not readable by native Windows applications.

See https://cygwin.com/cygwin-ug-net/using.html#pathnames-symlinks for details
on the default behavior and all the different symlink modes that are available
in Cygwin.

All these modes can also be enabled in MSYS2 by setting the `MSYS` environment
variable instead of `CYGWIN`. For example `MSYS=winsymlinks:nativestrict` can be
used to enable native Windows symlinks.

* `ln -s target link` - create a symlink to a file or directory

The default mode in MSYS2 is called `winsymlinks:deepcopy` and is only available
in MSYS2 and not in Cygwin.

## Native Windows Symlinks

This is a short primer for native Windows symlinks in case you haven't used them
before or are only familiar with them on Unix systems.

Symlink support in Windows existed since Windows Vista, but creating them was
initially only possible with administrator accounts. Starting with Windows 10
(~2016) it is possible to create symlinks with your normal user account if you
have the ["Developer Mode"
enabled](https://learn.microsoft.com/en-us/windows/apps/get-started/enable-your-device-for-development).
Existing symlinks that were created by an administrator account, or with the
"Developer Mode" enabled, can be used and deleted by normal users without the right
to create symlinks themselves.

Compared to Unix symlinks, the Windows symlinks come in a "directory" and a
"file" type. If you create a file symlink to a directory, or vice versa, or if
the target type changes after the symlink is created, the symlink will be
broken.

Symlinks also only work on NTFS/ReFS filesystems, and not on FAT32 or exFAT.

To create a symlink on Windows in cmd you can use "mklink":

* `mklink link target` - create a symlink called "link" to a file called "target"
* `mklink /d link target` - create a symlink called "link" to a directory called "target"

mklink ignores the type of any existing target and always creates a file
symlink if not specified otherwise.

* When on an unsupported filesystem you will get: "The device does not support symbolic links".
* Without developer mode enabled or administrative privileges you will get: "You do not have sufficient privilege to perform this operation."

Under PowerShell you can use "New-Item":

* `New-Item -ItemType SymbolicLink -Path link -Target target` - create a symlink called "link" to a file or directory called "target"

Unlike "mklink", "New-Item" will automatically create a directory symlink if the
target is a directory and a file symlink if the target is a file. In case the
target doesn't exist yet, the symlink will be a file symlink by default and, as
far as I'm aware (??), there is no way to create a directory symlink to a
non-existing target with "New-Item".

* When on an unsupported filesystem you will get: "Symbolic links are not supported for the specified path."
* Without developer mode enabled or administrative privileges: "Administrator privilege required for this operation."

If you get the "Administrator privilege required" error despite having Developer
Mode enabled, you might be using a too old version of Powershell, like the
version included in Windows by default. You either need to [install a newer
version of Powershell](https://winget.run/pkg/Microsoft/PowerShell), use mklink
via `cmd /c mklink`, or use an elevated prompt. You can check your Powershell
version with `$PSVersionTable.PSVersion`.

There also exist other types of links on Windows, like hardlinks and junctions,
which don't require special privileges to create, but have more limitations:

* [Hardlinks](https://learn.microsoft.com/en-us/windows/win32/fileio/hard-links-and-junctions#hard-links) (`mklink /h`) can only be created for files and only on the same filesystem, among other limitations.
* [Junctions](https://learn.microsoft.com/en-us/windows/win32/fileio/hard-links-and-junctions#junctions) (`mklink /j`) can only be created for directories and can't use relative paths, among other limitations.

## Known Issues

For the default `winsymlinks:deepcopy` mode in MSYS2 the target needs to exist
for the symlinking to work. While dangling symlinks are not that common, they
can happen easily when unpacking a tar file that contains symlinks. If the
symlinks is unpacked before the target, then creating the symlink will fail. A
workaround for this is to unpack the tar file twice, first to create the target
while ignoring any symlink creation errors and then to create the symlinks:

```bash
bsdtar -xf "archive.tar.bz2" 2>/dev/null || bsdtar -xf "archive.tar.bz2"
```