Git Bash shell fails to create symbolic links
MsysgitMsysMsysgit Problem Overview
When I try to create a symbolic link from the Git Bash shell, it fails every time all the time:
ln -s /c/Users/bzisad0/Work testlink
Output:
ln: creating symbolic link `testlink' to `/c/Users/bzisad0/Work': Permission denied
The only thing it does, besides giving the error message, is create an empty directory named (in this case) testlink
.
I don't see any problem with the ln
executable. For instance, it is owned by me and marked as executable:
which ln
ls -hal /bin/ln
Output:
/bin/ln
-rwxr-xr-x 1 BZISAD0 Administ 71k Sep 5 11:55 /bin/ln
I also own the current directory (~
, which is /c/Users/bzisad0
):
ls -dhal .
Output:
drwxr-xr-x 115 BZISAD0 Administ 40k Sep 5 12:23 .
I have administrative rights, and I've tried opening the Git Bash shell with "Run as Administrator", but that makes no difference.
I've tried opening the Windows properties for ln.exe
and setting the Privilege Level to "Run this program as an administrator" but that doesn't help.
I've gone into the Security → Advanced properties in Windows and made myself (rather than the Administrators group) the owner, but that doesn't fix anything either.
I'm at a loss. I don't know whether this error message is ultimately coming from ln
, from Bash, or from Windows, or how I could possibly lack the permission. How can I get to the bottom of this?
Msysgit Solutions
Solution 1 - Msysgit
It is possible, albeit extremely awkward, to create a symlink in MSYSGIT.
First, we need to make sure we are on Windows. Here's an example function to check that:
windows() { [[ -n "$WINDIR" ]]; }
Now, we can't do cmd /C
, because MSYSGIT will fornicate with this argument and turn it into C:
. Also, don't be tempted to use /K
, it only works if you don't have a K:
drive.
So while it will replace this value on program arguments, it won't on heredocs. We can use this to our advantage:
if windows; then
cmd <<< "mklink /D \"${link%/}\" \"${target%/}\"" > /dev/null
else
ln -s "$target" "$link"
fi
Also: note that I included /D
because I'm interested in directory symlinks only; Windows has that distinction. With plenty of effort, you could write a ln() { ... }
function that wraps the Windows API and serves as a complete drop-in solution, but that's... left as an exercise for the reader.
Edit: As a thank-you for the accepted answer, here's a more comprehensive function.
# We still need this.
windows() { [[ -n "$WINDIR" ]]; }
# Cross-platform symlink function. With one parameter, it will check
# whether the parameter is a symlink. With two parameters, it will create
# a symlink to a file or directory, with syntax: link $linkname $target
link() {
if [[ -z "$2" ]]; then
# Link-checking mode.
if windows; then
fsutil reparsepoint query "$1" > /dev/null
else
[[ -h "$1" ]]
fi
else
# Link-creation mode.
if windows; then
# Windows needs to be told if it's a directory or not. Infer that.
# Also: note that we convert `/` to `\`. In this case it's necessary.
if [[ -d "$2" ]]; then
cmd <<< "mklink /D \"$1\" \"${2//\//\\}\"" > /dev/null
else
cmd <<< "mklink \"$1\" \"${2//\//\\}\"" > /dev/null
fi
else
# You know what? I think ln's parameters are backwards.
ln -s "$2" "$1"
fi
fi
}
Also note a few things:
- I just wrote this and briefly tested it on Win7 and Ubuntu, give it a try first if you're from 2015 and using Windows 9.
- NTFS has reparse points and junction points. I chose reparse points because it's more of an actual symlink and works for files or directories, but junction points would have the benefit of being an usable solution in XP, except it's just for directories.
- Some filesystems, the FAT ones in particular, do not support symlinks. Modern Windows versions do not support booting from them anymore, but Windows and Linux can mount them.
Bonus function: remove a link.
# Remove a link, cross-platform.
rmlink() {
if windows; then
# Again, Windows needs to be told if it's a file or directory.
if [[ -d "$1" ]]; then
rmdir "$1";
else
rm "$1"
fi
else
rm "$1"
fi
}
Solution 2 - Msysgit
For my setup, that is Git for Windows 2.11.0 installed on Windows 8.1 export MSYS=winsymlinks:nativestrict
does the trick as explained here: https://github.com/git-for-windows/git/pull/156
It's important to launch the Git Bash shell as administrator as on Windows only administrators could create the symbolic links.
So, in order to make tar -xf
work and create the required symlinks:
- Run Git Bash shell as an administrator
- Run
export MSYS=winsymlinks:nativestrict
- Run tar
Solution 3 - Msysgit
A workaround is to run mklink
from Bash. This also allows you to create either a symbolic link or a junction point.
Take care to send the mklink
command as a single argument to cmd
...
cmd /c "mklink link target"
Here are the options for mklink
...
cmd /c mklink
Output:
Creates a symbolic link.
MKLINK [[/D] | [/H] | [/J]] Link Target
/D Creates a directory symbolic link. Default is a file
symbolic link.
/H Creates a hard link instead of a symbolic link.
/J Creates a Directory Junction.
Link specifies the new symbolic link name.
Target specifies the path (relative or absolute) that the new link
refers to.
If you want to create links via a GUI instead ... I recommend Link Shell Extension that is a Windows Explorer plugin for creating symbolic links, hard links, junction points, and volume mount points. I've been using it for years!
Symbolic links can be a life saver if you have a smaller SSD drive on your system C: drive and need to symbolic link some bloated folders that don't need to be on SSD, but off onto other drives. I use the free WinDirStat to find the disk space hogs.
Solution 4 - Msysgit
I believe that the ln
that shipped with MSysGit simply tries to copy its arguments, rather than fiddle with links. This is because links only work (sort of) on NTFS filesystems, and the MSYS team didn't want to reimplement ln.
See, for example, http://mingw.5.n7.nabble.com/symbolic-link-to-My-Documents-in-MSYS-td28492.html
Solution 5 - Msysgit
Do
Grant yourself privileges' to create symlinks.
- Search for
local security policies
Local Policies/User Rights Assignment/Create symbolic links
- Take a moment to scold Windows. "Bad OS! Bad!"
- Profit
This grants you the pricledge to create symlinks. Note, this takes effect on next login.
The next step is to figure out how ln
is configured
env | grep MSYS
What we are looking for is MSYS=winsymlink:
which controls how ln
creates symlinks.
If the variable doesn't exist, create it. Note, this will overwrite the existing MSYS enviroment variable.
setx MSYS winsymlinks:nativestrict
Do not
Run your shell as an administrator just to create symlinks.
Explanation
The error is somewhat self-explanatory, yet elusive.
You lack the appropriate privileges' to run the command.
Why?
Be default, windows only grants symlink creation rights to Administrators?
CYGWIN has to do a song and dance to get around Windows subpar treatment of symlinks.
Why?
Something, something "secturity"
¯\_(ツ)_/¯
Edit:
I just realized OP had admin rights. I leave this answer up, hoping it's useful to others.
Solution 6 - Msysgit
Extending Camilo Martin's anwser as you need to use the /j parameter switch for Windows 10; otherwise the call will just return "You do not have sufficient privilege to perform this operation."
This works for git bash 2.20.1.windows.1/MINGW64 (Windows 10) without Admin rights (if you can read/write both /old/path and /link/path:
original_folder=$(cygpath -w "/old/path")
create_link_new_folder=$(cygpath -w "/link/path")
cmd <<< "mklink /j \"${create_link_new_folder}\" \"${original_folder}\"" > /dev/null
Solution 7 - Msysgit
for anyone who's interested in how to acomplish this in Windows 10 Git Bash 2.28.0.0.1,
You have to prefix the ln -s
command with the MSYS=..
instead of execute export MSYS=..
first, namely it's just one command:
MSYS=winsymlinks:nativestrict ln -s <TARGET> <NEW_LINK_NAME>
Solution 8 - Msysgit
Since this is one of the top links that come up when searching for creating symlinks in Msys or git bash, I found the answer was to add
set MSYS=winsymlinks:native
when calling git-cmd.exe
(I run ConEmu) or uncomment the same line in the msys2_shell.bat
Solution 9 - Msysgit
I prefer Powershell to CMD, and thought i'd share the powershell version of this.
In my case it consists of making symlinks linking ~/.$file to ~/dotfiles/$file,
for dotfile configurations. I put this inside a .sh
script and ran it with git-bash:
powershell New-Item -ItemType SymbolicLink\
-Path \$Home/.$file\
-Target \$Home/dotfiles/$file
Solution 10 - Msysgit
Instead of symbolic links on windows, I found it easier to write small bash script that I place in my ~/bin catalog.
To start notepad++ with the npp
command I have this file:
$ cat ~/bin/npp
#!/usr/bin/bash
'/c/Program Files (x86)/Notepad++/notepad++.exe' $@
And I get the path syntax right by drag and drop the file from explorer into vim.
The windows command mklink /J Link Target
doesn't seem to work anymore.