|
| 1 | +# Symlinks |
| 2 | + |
| 3 | +[Symlinks](https://en.wikipedia.org/wiki/Symbolic_link) are a way to create a |
| 4 | +reference to a file or directory in another location. They are quite common on |
| 5 | +Linux/Unix systems, and many Unix tools are using/depending on them. While |
| 6 | +symlinks also exist on Windows, they are not available to normal Windows users |
| 7 | +by default, which makes them less common on Windows. |
| 8 | + |
| 9 | +In MSYS2 symlinks are by default avoided, and creating a symlink with Cygwin |
| 10 | +tools instead creates a deep-copy of the target. This makes sure that symlinking |
| 11 | +works on every Windows installation and for every Windows user and the result is |
| 12 | +readable by native Windows software. |
| 13 | + |
| 14 | +The deep-copying has the downside that the symlink and the target are not |
| 15 | +actually linked, and due to the duplication it also takes up double the disk |
| 16 | +space. Another downside is that the target always needs to exist, otherwise it |
| 17 | +couldn't be copied. |
| 18 | + |
| 19 | +We are planning to improve this in the future, see |
| 20 | +https://github.com/msys2/msys2-runtime/pull/114 for more information. |
| 21 | + |
| 22 | +## Cygwin Symlink Modes |
| 23 | + |
| 24 | +While MSYS2 is based on Cygwin, Cygwin has different default behavior for |
| 25 | +symlinks and tries to create "real" symlinks if possible, with the potential |
| 26 | +downside that they are not readable by native Windows applications. |
| 27 | + |
| 28 | +See https://cygwin.com/cygwin-ug-net/using.html#pathnames-symlinks for details |
| 29 | +on the default behavior and all the different symlink modes that are available |
| 30 | +in Cygwin. |
| 31 | + |
| 32 | +All these modes can also be enabled in MSYS2 by setting the `MSYS` environment |
| 33 | +variable instead of `CYGWIN`. For example `MSYS=winsymlinks:nativestrict` can be |
| 34 | +used to enable native Windows symlinks. |
| 35 | + |
| 36 | +* `ln -s target link` - create a symlink to a file or directory |
| 37 | + |
| 38 | +The default mode in MSYS2 is called `winsymlinks:deepcopy` and is only available |
| 39 | +in MSYS2 and not in Cygwin. |
| 40 | + |
| 41 | +## Native Windows Symlinks |
| 42 | + |
| 43 | +This is a short primer for native Windows symlinks in case you haven't used them |
| 44 | +before or are only familiar with them on Unix systems. |
| 45 | + |
| 46 | +Symlink support in Windows existed for a long time, but creating them was |
| 47 | +initially only possible with administrator accounts. Starting with Windows 10 |
| 48 | +(~2016) it is now possible to create symlinks with your normal user account if |
| 49 | +you have the ["Developer Mode" |
| 50 | +enabled](https://learn.microsoft.com/en-us/windows/apps/get-started/enable-your-device-for-development). |
| 51 | +Existing symlinks that were created by an administrator account, or with the |
| 52 | +"Developer Mode" enabled, can be used and deleted by normal users without rights |
| 53 | +to create symlinks themselves. |
| 54 | + |
| 55 | +Compare to Unix symlinks, the Windows symlinks come in a "directory" and a |
| 56 | +"file" type. If you create a file symlink to a directory, or vice versa, or if |
| 57 | +the target type changes after the symlink is created, the symlink will be |
| 58 | +broken. |
| 59 | + |
| 60 | +Symlinks also only work on NTFS/ReFS filesystems, and not on FAT32 or exFAT. |
| 61 | + |
| 62 | +To create a symlink on Windows in cmd you can use "mklink": |
| 63 | + |
| 64 | +* `mklink link target` - create a symlink called "link" to a file called "target" |
| 65 | +* `mklink /d link target` - create a symlink called "link" to a directory called "target" |
| 66 | + |
| 67 | +mklink ignores the type of any existing target and always creates a file |
| 68 | +symlink if not specified otherwise. |
| 69 | + |
| 70 | +* When on an unsupported filesystem you will get: "The device does not support symbolic links". |
| 71 | +* Without developer mode enabled or administrative privileges you will get: "You do not have sufficient privilege to perform this operation." |
| 72 | + |
| 73 | +Under PowerShell you can use "New-Item": |
| 74 | + |
| 75 | +* `New-Item -ItemType SymbolicLink -Path link -Target target` - create a symlink called "link" to a file or directory called "target" |
| 76 | + |
| 77 | +Unlike "mklink", "New-Item" will automatically create a directory symlink if the |
| 78 | +target is a directory and a file symlink if the target is a file. In case the |
| 79 | +target doesn't exist yet, the symlink will be a file symlink by default and, as |
| 80 | +far as I'm aware (??), there is no way to create a directory symlink to a |
| 81 | +non-existing target with "New-Item". |
| 82 | + |
| 83 | +* When on an unsupported filesystem you will get: "Symbolic links are not supported for the specified path." |
| 84 | +* Without developer mode enabled or administrative privileges: "Administrator privilege required for this operation." |
| 85 | + |
| 86 | +If you get the "Administrator privilege required" error despite having Developer |
| 87 | +Mode enabled, you might be using a too old version of Powershell, like the |
| 88 | +version included in Windows by default. You either need to [install a newer |
| 89 | +version of Powershell](https://winget.run/pkg/Microsoft/PowerShell), use mklink |
| 90 | +via `cmd /c mklink`, or use an elevated prompt. You can check your Powershell |
| 91 | +version with `$PSVersionTable.PSVersion`. |
| 92 | + |
| 93 | +## Known Issues |
| 94 | + |
| 95 | +As stated above, for the deep-copy to work the target needs to exist for the |
| 96 | +symlinking to work. While dangling symlinks are not that common, they can happen |
| 97 | +easily when unpacking a tar file that contains symlinks. If the symlinks is |
| 98 | +unpacked before the target, then creating the symlink will fail. A workaround |
| 99 | +for this is to unpack the tar file twice, first to create the target while |
| 100 | +ignoring any symlink creation errors and then to create the symlinks: |
| 101 | + |
| 102 | +```bash |
| 103 | +bsdtar -xf "archive.tar.bz2" 2>/dev/null || bsdtar -xf "archive.tar.bz2" |
| 104 | +``` |
0 commit comments