--- /dev/null
+// Copyright 2018 The Go Authors. All rights reserved.\r
+// Use of this source code is governed by a BSD-style\r
+// license that can be found in the LICENSE file.\r
+\r
+package windows\r
+\r
+import "syscall"\r
+\r
+const (\r
+ ERROR_INVALID_PARAMETER syscall.Errno = 87\r
+\r
+ // symlink support for CreateSymbolicLink() starting with Windows 10 (1703, v10.0.14972)\r
+ SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE = 0x2\r
+)\r
return &LinkError{"symlink", oldname, newname, err}
}
- var flags uint32
+ var flags uint32 = windows.SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE
if isdir {
flags |= syscall.SYMBOLIC_LINK_FLAG_DIRECTORY
}
err = syscall.CreateSymbolicLink(n, o, flags)
+
+ if err != nil {
+ // the unprivileged create flag is unsupported
+ // below Windows 10 (1703, v10.0.14972). retry without it.
+ flags &^= windows.SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE
+
+ err = syscall.CreateSymbolicLink(n, o, flags)
+ }
+
if err != nil {
return &LinkError{"symlink", oldname, newname, err}
}
t.Errorf(`"NUL" and "nul" are not the same file`)
}
}
+
+// TestSymlinkCreation verifies that creating a symbolic link
+// works on Windows when developer mode is active.
+// This is supported starting Windows 10 (1703, v10.0.14972).
+func TestSymlinkCreation(t *testing.T) {
+ if !isWindowsDeveloperModeActive() {
+ t.Skip("Windows developer mode is not active")
+ }
+
+ temp, err := ioutil.TempDir("", "TestSymlinkCreation")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(temp)
+
+ dummyFile := filepath.Join(temp, "file")
+ err = ioutil.WriteFile(dummyFile, []byte(""), 0644)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ linkFile := filepath.Join(temp, "link")
+ err = os.Symlink(dummyFile, linkFile)
+ if err != nil {
+ t.Fatal(err)
+ }
+}
+
+// isWindowsDeveloperModeActive checks whether or not the developer mode is active on Windows 10.
+// Returns false for prior Windows versions.
+// see https://docs.microsoft.com/en-us/windows/uwp/get-started/enable-your-device-for-development
+func isWindowsDeveloperModeActive() bool {
+ key, err := registry.OpenKey(registry.LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\AppModelUnlock", registry.READ)
+ if err != nil {
+ return false
+ }
+
+ val, _, err := key.GetIntegerValue("AllowDevelopmentWithoutDevLicense")
+ if err != nil {
+ return false
+ }
+
+ return val != 0
+}