From 77812a30bd8e09cb62f79c996c006f0faf1ce605 Mon Sep 17 00:00:00 2001 From: Felix Kollmann Date: Thu, 8 Mar 2018 03:15:10 +0100 Subject: [PATCH 01/11] os/file: enable symlink creation o Windows 10 fixes https://github.com/golang/go/issues/22874 --- src/os/file_windows.go | 2 +- src/syscall/types_windows.go | 13 +++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/os/file_windows.go b/src/os/file_windows.go index 91918301443b9..2a1a07815ca6e 100644 --- a/src/os/file_windows.go +++ b/src/os/file_windows.go @@ -371,7 +371,7 @@ func Symlink(oldname, newname string) error { return &LinkError{"symlink", oldname, newname, err} } - var flags uint32 + var flags uint32 = syscall.SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE if isdir { flags |= syscall.SYMBOLIC_LINK_FLAG_DIRECTORY } diff --git a/src/syscall/types_windows.go b/src/syscall/types_windows.go index 59bfe5d642724..389d816ad1862 100644 --- a/src/syscall/types_windows.go +++ b/src/syscall/types_windows.go @@ -1113,10 +1113,11 @@ type reparseDataBuffer struct { } const ( - FSCTL_GET_REPARSE_POINT = 0x900A8 - MAXIMUM_REPARSE_DATA_BUFFER_SIZE = 16 * 1024 - _IO_REPARSE_TAG_MOUNT_POINT = 0xA0000003 - IO_REPARSE_TAG_SYMLINK = 0xA000000C - SYMBOLIC_LINK_FLAG_DIRECTORY = 0x1 - _SYMLINK_FLAG_RELATIVE = 1 + FSCTL_GET_REPARSE_POINT = 0x900A8 + MAXIMUM_REPARSE_DATA_BUFFER_SIZE = 16 * 1024 + _IO_REPARSE_TAG_MOUNT_POINT = 0xA0000003 + IO_REPARSE_TAG_SYMLINK = 0xA000000C + SYMBOLIC_LINK_FLAG_DIRECTORY = 0x1 + SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE = 0x2 + _SYMLINK_FLAG_RELATIVE = 1 ) From f1df0d3a950cdd2236a06bab5dbc7197ca2c7958 Mon Sep 17 00:00:00 2001 From: Felix Kollmann Date: Thu, 8 Mar 2018 15:15:22 +0100 Subject: [PATCH 02/11] test: added test for fix #22874 --- test/fixedbugs/issue22874.go | 93 ++++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 test/fixedbugs/issue22874.go diff --git a/test/fixedbugs/issue22874.go b/test/fixedbugs/issue22874.go new file mode 100644 index 0000000000000..fd7265d5b3e3f --- /dev/null +++ b/test/fixedbugs/issue22874.go @@ -0,0 +1,93 @@ +package main + +import ( + "fmt" + "io/ioutil" + "os" + "os/exec" + "path/filepath" + "runtime" + "strings" + "syscall" +) + +// https://github.com/golang/go/issues/22874 +// os: Symlink creation should work on Windows without elevation + +func main() { + // run on windows, only + if runtime.GOOS != "windows" { + fmt.Println("Skipping test on non-Windows system") + return + } + + // run test + err := test() + + if err != nil { + panic(err) + } +} + +func test() error { + // is developer mode active? + // the expected result depends on it + devMode, _ := isDeveloperModeActive() + + fmt.Printf("Windows developer mode active: %v\n", devMode) + + // create dummy file to symlink + dummyFile := filepath.Join(os.TempDir(), "issue22874.test") + + err := ioutil.WriteFile(dummyFile, []byte(""), 0644) + + if err != nil { + return fmt.Errorf("Failed to create dummy file: %v", err) + } + + defer os.Remove(dummyFile) + + // create the symlink + linkFile := fmt.Sprintf("%v.link", dummyFile) + + err = os.Symlink(dummyFile, linkFile) + + if err != nil { + // only the ERROR_PRIVILEGE_NOT_HELD error is allowed + if x, ok := err.(*os.LinkError); ok { + if xx, ok := x.Err.(syscall.Errno); ok { + + if xx == syscall.ERROR_PRIVILEGE_NOT_HELD { + // is developer mode active? + if devMode { + return fmt.Errorf("Windows developer mode is active, but creating symlink failed with ERROR_PRIVILEGE_NOT_HELD anyway: %v", err) + } + + // developer mode is disabled, and the error is expected + fmt.Printf("Success: Creating symlink failed with expected ERROR_PRIVILEGE_NOT_HELD error\n") + + return nil + } + } + } + + return fmt.Errorf("Failed to create symlink: %v", err) + } + + // remove the link. don't care for any errors + os.Remove(linkFile) + + fmt.Printf("Success: Creating symlink succeeded\n") + + return nil +} + +func isDeveloperModeActive() (bool, error) { + result, err := exec.Command("reg.exe", "query", "HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\AppModelUnlock", "/v", "AllowDevelopmentWithoutDevLicense").Output() + + if err != nil { + return false, err + } + + return strings.Contains(string(result), "0x1"), nil +} From 5b1ae4a1f7db5bbcf197daf6987ab6a3f55a2bd0 Mon Sep 17 00:00:00 2001 From: Felix Kollmann Date: Thu, 8 Mar 2018 16:59:28 +0100 Subject: [PATCH 03/11] os/file: fixed compatibility with older Windows versions --- src/os/file_windows.go | 11 +++++++++++ src/syscall/types_windows.go | 1 + 2 files changed, 12 insertions(+) diff --git a/src/os/file_windows.go b/src/os/file_windows.go index 2a1a07815ca6e..6f79a35f4a55b 100644 --- a/src/os/file_windows.go +++ b/src/os/file_windows.go @@ -376,6 +376,17 @@ func Symlink(oldname, newname string) error { flags |= syscall.SYMBOLIC_LINK_FLAG_DIRECTORY } err = syscall.CreateSymbolicLink(n, o, flags) + + // creating symlinks unelevated is unsupported + // below Windows 10 (1607, v10.0.14393). + // retry without the SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE + if err == syscall.ERROR_INVALID_PARAMETER { + flags &^= syscall.SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE + + err = syscall.CreateSymbolicLink(n, o, flags) + } + + // handle error if err != nil { return &LinkError{"symlink", oldname, newname, err} } diff --git a/src/syscall/types_windows.go b/src/syscall/types_windows.go index 389d816ad1862..e9ec72107ec96 100644 --- a/src/syscall/types_windows.go +++ b/src/syscall/types_windows.go @@ -13,6 +13,7 @@ const ( ERROR_HANDLE_EOF Errno = 38 ERROR_NETNAME_DELETED Errno = 64 ERROR_FILE_EXISTS Errno = 80 + ERROR_INVALID_PARAMETER Errno = 87 ERROR_BROKEN_PIPE Errno = 109 ERROR_BUFFER_OVERFLOW Errno = 111 ERROR_INSUFFICIENT_BUFFER Errno = 122 From 5e1abfb2efb80fe6d18ede74234b8811d2da7dd1 Mon Sep 17 00:00:00 2001 From: Felix Kollmann Date: Fri, 9 Mar 2018 14:05:05 +0100 Subject: [PATCH 04/11] os: improved code based on review feedback - moved syscall changes to internal/syscall/windows - moved unit test to os package - unit test is now using direct windows regisrtry access --- .../syscall/windows/symlink_windows.go | 14 ++ src/os/file_windows.go | 12 +- .../os/file_windows_test.go | 180 +++++++++--------- src/syscall/types_windows.go | 14 +- 4 files changed, 112 insertions(+), 108 deletions(-) create mode 100644 src/internal/syscall/windows/symlink_windows.go rename test/fixedbugs/issue22874.go => src/os/file_windows_test.go (55%) diff --git a/src/internal/syscall/windows/symlink_windows.go b/src/internal/syscall/windows/symlink_windows.go new file mode 100644 index 0000000000000..78ae124ce9718 --- /dev/null +++ b/src/internal/syscall/windows/symlink_windows.go @@ -0,0 +1,14 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package windows + +import "syscall" + +const ( + ERROR_INVALID_PARAMETER syscall.Errno = 87 + + // symlink support for CreateSymbolicLink() starting with Windows 10 (1607, v10.0.14393) + SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE = 0x2 +) diff --git a/src/os/file_windows.go b/src/os/file_windows.go index 6f79a35f4a55b..bab5b0ed09c7c 100644 --- a/src/os/file_windows.go +++ b/src/os/file_windows.go @@ -371,22 +371,20 @@ func Symlink(oldname, newname string) error { return &LinkError{"symlink", oldname, newname, err} } - var flags uint32 = syscall.SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE + var flags uint32 = windows.SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE if isdir { flags |= syscall.SYMBOLIC_LINK_FLAG_DIRECTORY } err = syscall.CreateSymbolicLink(n, o, flags) - // creating symlinks unelevated is unsupported - // below Windows 10 (1607, v10.0.14393). - // retry without the SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE - if err == syscall.ERROR_INVALID_PARAMETER { - flags &^= syscall.SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE + if err == windows.ERROR_INVALID_PARAMETER { + // the unprivileged create flag is unsupported + // below Windows 10 (1607, v10.0.14393). retry without it. + flags &^= windows.SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE err = syscall.CreateSymbolicLink(n, o, flags) } - // handle error if err != nil { return &LinkError{"symlink", oldname, newname, err} } diff --git a/test/fixedbugs/issue22874.go b/src/os/file_windows_test.go similarity index 55% rename from test/fixedbugs/issue22874.go rename to src/os/file_windows_test.go index fd7265d5b3e3f..ab12fccd0a67c 100644 --- a/test/fixedbugs/issue22874.go +++ b/src/os/file_windows_test.go @@ -1,93 +1,87 @@ -package main - -import ( - "fmt" - "io/ioutil" - "os" - "os/exec" - "path/filepath" - "runtime" - "strings" - "syscall" -) - -// https://github.com/golang/go/issues/22874 -// os: Symlink creation should work on Windows without elevation - -func main() { - // run on windows, only - if runtime.GOOS != "windows" { - fmt.Println("Skipping test on non-Windows system") - return - } - - // run test - err := test() - - if err != nil { - panic(err) - } -} - -func test() error { - // is developer mode active? - // the expected result depends on it - devMode, _ := isDeveloperModeActive() - - fmt.Printf("Windows developer mode active: %v\n", devMode) - - // create dummy file to symlink - dummyFile := filepath.Join(os.TempDir(), "issue22874.test") - - err := ioutil.WriteFile(dummyFile, []byte(""), 0644) - - if err != nil { - return fmt.Errorf("Failed to create dummy file: %v", err) - } - - defer os.Remove(dummyFile) - - // create the symlink - linkFile := fmt.Sprintf("%v.link", dummyFile) - - err = os.Symlink(dummyFile, linkFile) - - if err != nil { - // only the ERROR_PRIVILEGE_NOT_HELD error is allowed - if x, ok := err.(*os.LinkError); ok { - if xx, ok := x.Err.(syscall.Errno); ok { - - if xx == syscall.ERROR_PRIVILEGE_NOT_HELD { - // is developer mode active? - if devMode { - return fmt.Errorf("Windows developer mode is active, but creating symlink failed with ERROR_PRIVILEGE_NOT_HELD anyway: %v", err) - } - - // developer mode is disabled, and the error is expected - fmt.Printf("Success: Creating symlink failed with expected ERROR_PRIVILEGE_NOT_HELD error\n") - - return nil - } - } - } - - return fmt.Errorf("Failed to create symlink: %v", err) - } - - // remove the link. don't care for any errors - os.Remove(linkFile) - - fmt.Printf("Success: Creating symlink succeeded\n") - - return nil -} - -func isDeveloperModeActive() (bool, error) { - result, err := exec.Command("reg.exe", "query", "HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\AppModelUnlock", "/v", "AllowDevelopmentWithoutDevLicense").Output() - - if err != nil { - return false, err - } - - return strings.Contains(string(result), "0x1"), nil -} +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package os + +import ( + "fmt" + "internal/syscall/windows/registry" + "io/ioutil" + "os" + "path/filepath" + "syscall" + "testing" +) + +// see https://github.com/golang/go/issues/22874 +// os: Symlink creation should work on Windows without elevation + +func TestSymlink(t *testing.T) { + // is developer mode active? + // the expected result depends on it + devMode, _ := isDeveloperModeActive() + + t.Logf("Windows developer mode active: %v\n", devMode) + + // create dummy file to symlink + dummyFile := filepath.Join(os.TempDir(), "issue22874.test") + + err := ioutil.WriteFile(dummyFile, []byte(""), 0644) + + if err != nil { + t.Fatalf("Failed to create dummy file: %v", err) + } + + defer os.Remove(dummyFile) + + // create the symlink + linkFile := fmt.Sprintf("%v.link", dummyFile) + + err = os.Symlink(dummyFile, linkFile) + + if err != nil { + // only the ERROR_PRIVILEGE_NOT_HELD error is allowed + if x, ok := err.(*os.LinkError); ok { + if xx, ok := x.Err.(syscall.Errno); ok { + + if xx == syscall.ERROR_PRIVILEGE_NOT_HELD { + // is developer mode active? + if devMode { + t.Fatalf("Windows developer mode is active, but creating symlink failed with ERROR_PRIVILEGE_NOT_HELD anyway: %v", err) + } + + // developer mode is disabled, and the error is expected + fmt.Printf("Success: Creating symlink failed with expected ERROR_PRIVILEGE_NOT_HELD error\n") + + return nil + } + } + } + + t.Fatalf("Failed to create symlink: %v", err) + } + + // remove the link. don't care for any errors + os.Remove(linkFile) + + t.Logf("Success: Creating symlink succeeded\n") + + return nil +} + +func isDeveloperModeActive() (bool, error) { + key, err := registry.OpenKey(registry.LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\AppModelUnlock", registry.READ) + + if err != nil { + return false, err + } + + val, _, err := key.GetIntegerValue("AllowDevelopmentWithoutDevLicense") + + if err != nil { + return false, err + } + + return val != 0, nil +} diff --git a/src/syscall/types_windows.go b/src/syscall/types_windows.go index e9ec72107ec96..59bfe5d642724 100644 --- a/src/syscall/types_windows.go +++ b/src/syscall/types_windows.go @@ -13,7 +13,6 @@ const ( ERROR_HANDLE_EOF Errno = 38 ERROR_NETNAME_DELETED Errno = 64 ERROR_FILE_EXISTS Errno = 80 - ERROR_INVALID_PARAMETER Errno = 87 ERROR_BROKEN_PIPE Errno = 109 ERROR_BUFFER_OVERFLOW Errno = 111 ERROR_INSUFFICIENT_BUFFER Errno = 122 @@ -1114,11 +1113,10 @@ type reparseDataBuffer struct { } const ( - FSCTL_GET_REPARSE_POINT = 0x900A8 - MAXIMUM_REPARSE_DATA_BUFFER_SIZE = 16 * 1024 - _IO_REPARSE_TAG_MOUNT_POINT = 0xA0000003 - IO_REPARSE_TAG_SYMLINK = 0xA000000C - SYMBOLIC_LINK_FLAG_DIRECTORY = 0x1 - SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE = 0x2 - _SYMLINK_FLAG_RELATIVE = 1 + FSCTL_GET_REPARSE_POINT = 0x900A8 + MAXIMUM_REPARSE_DATA_BUFFER_SIZE = 16 * 1024 + _IO_REPARSE_TAG_MOUNT_POINT = 0xA0000003 + IO_REPARSE_TAG_SYMLINK = 0xA000000C + SYMBOLIC_LINK_FLAG_DIRECTORY = 0x1 + _SYMLINK_FLAG_RELATIVE = 1 ) From a4b78cfb00ac116f77c7f47d178b1e7f49a03ca8 Mon Sep 17 00:00:00 2001 From: Felix Kollmann Date: Fri, 9 Mar 2018 14:08:30 +0100 Subject: [PATCH 05/11] os: fixed 'file' unit test logging on windows --- src/os/file_windows_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/os/file_windows_test.go b/src/os/file_windows_test.go index ab12fccd0a67c..ad903d3d2767e 100644 --- a/src/os/file_windows_test.go +++ b/src/os/file_windows_test.go @@ -22,7 +22,7 @@ func TestSymlink(t *testing.T) { // the expected result depends on it devMode, _ := isDeveloperModeActive() - t.Logf("Windows developer mode active: %v\n", devMode) + t.Logf("Windows developer mode active: %v", devMode) // create dummy file to symlink dummyFile := filepath.Join(os.TempDir(), "issue22874.test") @@ -52,7 +52,7 @@ func TestSymlink(t *testing.T) { } // developer mode is disabled, and the error is expected - fmt.Printf("Success: Creating symlink failed with expected ERROR_PRIVILEGE_NOT_HELD error\n") + t.Logf("Success: Creating symlink failed with expected ERROR_PRIVILEGE_NOT_HELD error") return nil } @@ -65,7 +65,7 @@ func TestSymlink(t *testing.T) { // remove the link. don't care for any errors os.Remove(linkFile) - t.Logf("Success: Creating symlink succeeded\n") + t.Logf("Success: Creating symlink succeeded") return nil } From 81c6e47b0d76eb87fcfc708fcd79cd56fa1fdffd Mon Sep 17 00:00:00 2001 From: Felix Kollmann Date: Mon, 12 Mar 2018 16:30:17 +0100 Subject: [PATCH 06/11] os: fixed typo in Windows version number --- src/internal/syscall/windows/symlink_windows.go | 2 +- src/os/file_windows.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/internal/syscall/windows/symlink_windows.go b/src/internal/syscall/windows/symlink_windows.go index 78ae124ce9718..59844d41157b3 100644 --- a/src/internal/syscall/windows/symlink_windows.go +++ b/src/internal/syscall/windows/symlink_windows.go @@ -9,6 +9,6 @@ import "syscall" const ( ERROR_INVALID_PARAMETER syscall.Errno = 87 - // symlink support for CreateSymbolicLink() starting with Windows 10 (1607, v10.0.14393) + // symlink support for CreateSymbolicLink() starting with Windows 10 (1703, v10.0.14972) SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE = 0x2 ) diff --git a/src/os/file_windows.go b/src/os/file_windows.go index bab5b0ed09c7c..17b6553635e7a 100644 --- a/src/os/file_windows.go +++ b/src/os/file_windows.go @@ -379,7 +379,7 @@ func Symlink(oldname, newname string) error { if err == windows.ERROR_INVALID_PARAMETER { // the unprivileged create flag is unsupported - // below Windows 10 (1607, v10.0.14393). retry without it. + // below Windows 10 (1703, v10.0.14972). retry without it. flags &^= windows.SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE err = syscall.CreateSymbolicLink(n, o, flags) From 87976152be3ddc0560b5f4cf438814f67b634836 Mon Sep 17 00:00:00 2001 From: Felix Kollmann Date: Tue, 13 Mar 2018 14:29:08 +0100 Subject: [PATCH 07/11] os: improved code based on review feedback - moved unit test to common os test file - separated developer mode and non-developer mode tests --- src/cmd/cover/testdata/test_line.go | 295 ++++++++++++++++++++++++++++ src/os/file_windows_test.go | 87 -------- src/os/os_windows_test.go | 100 ++++++++++ 3 files changed, 395 insertions(+), 87 deletions(-) create mode 100644 src/cmd/cover/testdata/test_line.go delete mode 100644 src/os/file_windows_test.go diff --git a/src/cmd/cover/testdata/test_line.go b/src/cmd/cover/testdata/test_line.go new file mode 100644 index 0000000000000..7cd9590ce48c8 --- /dev/null +++ b/src/cmd/cover/testdata/test_line.go @@ -0,0 +1,295 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This program is processed by the cover command, and then testAll is called. +// The test driver in main.go can then compare the coverage statistics with expectation. + +// The word 8 is replaced by the line number in this file. When the file is executed, +// the coverage processing has changed the line numbers, so we can't use runtime.Caller. + +package main + +import _ "unsafe" // for go:linkname + +//go:linkname some_name some_name + +const anything = 1e9 // Just some unlikely value that means "we got here, don't care how often" + +func testAll() { + testSimple() + testBlockRun() + testIf() + testFor() + testRange() + testSwitch() + testTypeSwitch() + testSelect1() + testSelect2() + testPanic() + testEmptySwitches() + testFunctionLiteral() + testGoto() +} + +// The indexes of the counters in testPanic are known to main.go +const panicIndex = 3 + +// This test appears first because the index of its counters is known to main.go +func testPanic() { + defer func() { + recover() + }() + check(43, 1) + panic("should not get next line") + check(45, 0) // this is GoCover.Count[panicIndex] + // The next counter is in testSimple and it will be non-zero. + // If the panic above does not trigger a counter, the test will fail + // because GoCover.Count[panicIndex] will be the one in testSimple. +} + +func testSimple() { + check(52, 1) +} + +func testIf() { + if true { + check(57, 1) + } else { + check(59, 0) + } + if false { + check(62, 0) + } else { + check(64, 1) + } + for i := 0; i < 3; i++ { + if checkVal(67, 3, i) <= 2 { + check(68, 3) + } + if checkVal(70, 3, i) <= 1 { + check(71, 2) + } + if checkVal(73, 3, i) <= 0 { + check(74, 1) + } + } + for i := 0; i < 3; i++ { + if checkVal(78, 3, i) <= 1 { + check(79, 2) + } else { + check(81, 1) + } + } + for i := 0; i < 3; i++ { + if checkVal(85, 3, i) <= 0 { + check(86, 1) + } else if checkVal(87, 2, i) <= 1 { + check(88, 1) + } else if checkVal(89, 1, i) <= 2 { + check(90, 1) + } else if checkVal(91, 0, i) <= 3 { + check(92, 0) + } + } + if func(a, b int) bool { return a < b }(3, 4) { + check(96, 1) + } +} + +func testFor() { + for i := 0; i < 10; func() { i++; check(101, 10) }() { + check(102, 10) + } +} + +func testRange() { + for _, f := range []func(){ + func() { check(108, 1) }, + } { + f() + check(111, 1) + } +} + +func testBlockRun() { + check(116, 1) + { + check(118, 1) + } + { + check(121, 1) + } + check(123, 1) + { + check(125, 1) + } + { + check(128, 1) + } + check(130, 1) +} + +func testSwitch() { + for i := 0; i < 5; func() { i++; check(134, 5) }() { + switch i { + case 0: + check(137, 1) + case 1: + check(139, 1) + case 2: + check(141, 1) + default: + check(143, 2) + } + } +} + +func testTypeSwitch() { + var x = []interface{}{1, 2.0, "hi"} + for _, v := range x { + switch func() { check(151, 3) }(); v.(type) { + case int: + check(153, 1) + case float64: + check(155, 1) + case string: + check(157, 1) + case complex128: + check(159, 0) + default: + check(161, 0) + } + } +} + +func testSelect1() { + c := make(chan int) + go func() { + for i := 0; i < 1000; i++ { + c <- i + } + }() + for { + select { + case <-c: + check(176, anything) + case <-c: + check(178, anything) + default: + check(180, 1) + return + } + } +} + +func testSelect2() { + c1 := make(chan int, 1000) + c2 := make(chan int, 1000) + for i := 0; i < 1000; i++ { + c1 <- i + c2 <- i + } + for { + select { + case <-c1: + check(196, 1000) + case <-c2: + check(198, 1000) + default: + check(200, 1) + return + } + } +} + +// Empty control statements created syntax errors. This function +// is here just to be sure that those are handled correctly now. +func testEmptySwitches() { + check(209, 1) + switch 3 { + } + check(212, 1) + switch i := (interface{})(3).(int); i { + } + check(215, 1) + c := make(chan int) + go func() { + check(218, 1) + c <- 1 + select {} + }() + <-c + check(223, 1) +} + +func testFunctionLiteral() { + a := func(f func()) error { + f() + f() + return nil + } + + b := func(f func()) bool { + f() + f() + return true + } + + check(239, 1) + a(func() { + check(241, 2) + }) + + if err := a(func() { + check(245, 2) + }); err != nil { + } + + switch b(func() { + check(250, 2) + }) { + } + + x := 2 + switch x { + case func() int { check(256, 1); return 1 }(): + check(257, 0) + panic("2=1") + case func() int { check(259, 1); return 2 }(): + check(260, 1) + case func() int { check(261, 0); return 3 }(): + check(262, 0) + panic("2=3") + } +} + +func testGoto() { + for i := 0; i < 2; i++ { + if i == 0 { + goto Label + } + check(272, 1) + Label: + check(274, 2) + } + // Now test that we don't inject empty statements + // between a label and a loop. +loop: + for { + check(280, 1) + break loop + } +} + +// This comment didn't appear in generated go code. +func haha() { + // Needed for cover to add counter increment here. + _ = 42 +} + +// Some someFunction. +// +//go:nosplit +func someFunction() { +} diff --git a/src/os/file_windows_test.go b/src/os/file_windows_test.go deleted file mode 100644 index ad903d3d2767e..0000000000000 --- a/src/os/file_windows_test.go +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright 2016 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package os - -import ( - "fmt" - "internal/syscall/windows/registry" - "io/ioutil" - "os" - "path/filepath" - "syscall" - "testing" -) - -// see https://github.com/golang/go/issues/22874 -// os: Symlink creation should work on Windows without elevation - -func TestSymlink(t *testing.T) { - // is developer mode active? - // the expected result depends on it - devMode, _ := isDeveloperModeActive() - - t.Logf("Windows developer mode active: %v", devMode) - - // create dummy file to symlink - dummyFile := filepath.Join(os.TempDir(), "issue22874.test") - - err := ioutil.WriteFile(dummyFile, []byte(""), 0644) - - if err != nil { - t.Fatalf("Failed to create dummy file: %v", err) - } - - defer os.Remove(dummyFile) - - // create the symlink - linkFile := fmt.Sprintf("%v.link", dummyFile) - - err = os.Symlink(dummyFile, linkFile) - - if err != nil { - // only the ERROR_PRIVILEGE_NOT_HELD error is allowed - if x, ok := err.(*os.LinkError); ok { - if xx, ok := x.Err.(syscall.Errno); ok { - - if xx == syscall.ERROR_PRIVILEGE_NOT_HELD { - // is developer mode active? - if devMode { - t.Fatalf("Windows developer mode is active, but creating symlink failed with ERROR_PRIVILEGE_NOT_HELD anyway: %v", err) - } - - // developer mode is disabled, and the error is expected - t.Logf("Success: Creating symlink failed with expected ERROR_PRIVILEGE_NOT_HELD error") - - return nil - } - } - } - - t.Fatalf("Failed to create symlink: %v", err) - } - - // remove the link. don't care for any errors - os.Remove(linkFile) - - t.Logf("Success: Creating symlink succeeded") - - return nil -} - -func isDeveloperModeActive() (bool, error) { - key, err := registry.OpenKey(registry.LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\AppModelUnlock", registry.READ) - - if err != nil { - return false, err - } - - val, _, err := key.GetIntegerValue("AllowDevelopmentWithoutDevLicense") - - if err != nil { - return false, err - } - - return val != 0, nil -} diff --git a/src/os/os_windows_test.go b/src/os/os_windows_test.go index 47e2611a40d8f..16d72abd279bd 100644 --- a/src/os/os_windows_test.go +++ b/src/os/os_windows_test.go @@ -8,9 +8,11 @@ import ( "fmt" "internal/poll" "internal/syscall/windows" + "internal/syscall/windows/registry" "internal/testenv" "io" "io/ioutil" + "math/rand" "os" osexec "os/exec" "path/filepath" @@ -20,6 +22,7 @@ import ( "strings" "syscall" "testing" + "time" "unicode/utf16" "unsafe" ) @@ -893,3 +896,100 @@ func main() { } } } + +// TestSymlinkWindowsDeveloperModeActive 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 TestSymlinkWindowsDeveloperModeActive(t *testing.T) { + if !isWindowsDeveloperModeActive() { + t.Skip("Windows developer mode is not active") + } + + // create dummy file to symlink + rand.Seed(time.Now().UnixNano()) + + dummyFile := filepath.Join(os.TempDir(), fmt.Sprintf("issue22874.test.%v", 1000000+rand.Int31n(8999999))) + err := ioutil.WriteFile(dummyFile, []byte(""), 0644) + if err != nil { + t.Fatalf("Failed to create dummy file: %v", err) + } + + defer os.Remove(dummyFile) + + // create the symlink + linkFile := fmt.Sprintf("%v.link", dummyFile) + err = os.Symlink(dummyFile, linkFile) + if err != nil { + // only the ERROR_PRIVILEGE_NOT_HELD error is allowed + if errLink, ok := err.(*os.LinkError); ok { + if errNo, ok := errLink.Err.(syscall.Errno); ok { + + if errNo == syscall.ERROR_PRIVILEGE_NOT_HELD { + t.Fatalf("Windows developer mode is active, but creating symlink failed with ERROR_PRIVILEGE_NOT_HELD anyway: %v", err) + } + } + } + + t.Fatalf("Failed to create symlink: %v", err) + } + // remove the link. don't care for any errors + os.Remove(linkFile) +} + +// TestSymlinkWindowsDeveloperModeInactive verifies that creating a symbolic link +// will fail with the correct error message if developer mode is inactive. +// This test also checks for backward compatibility. +func TestSymlinkWindowsDeveloperModeInactive(t *testing.T) { + if isWindowsDeveloperModeActive() { + t.Skip("Windows developer mode is active") + } + + // create dummy file to symlink + rand.Seed(time.Now().UnixNano()) + + dummyFile := filepath.Join(os.TempDir(), fmt.Sprintf("issue22874.test.%v", 1000000+rand.Int31n(8999999))) + err := ioutil.WriteFile(dummyFile, []byte(""), 0644) + if err != nil { + t.Fatalf("Failed to create dummy file: %v", err) + } + + defer os.Remove(dummyFile) + + // create the symlink + linkFile := fmt.Sprintf("%v.link", dummyFile) + err = os.Symlink(dummyFile, linkFile) + if err == nil { + os.Remove(linkFile) + t.Fatal("Creating the symlink should have failed") + } + + // only the ERROR_PRIVILEGE_NOT_HELD error is allowed + if errLink, ok := err.(*os.LinkError); ok { + if errNo, ok := errLink.Err.(syscall.Errno); ok { + + if errNo == syscall.ERROR_PRIVILEGE_NOT_HELD { + // this error is expected + return + } + } + } + + t.Fatalf("Creating symlink failed with unexpected error: %v", 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 +} From 729f9abcc1652ce83b730b4c75db0c64810b1454 Mon Sep 17 00:00:00 2001 From: Felix Kollmann Date: Tue, 13 Mar 2018 16:03:01 +0100 Subject: [PATCH 08/11] Merge branch 'master' into konsorten/master --- src/cmd/cover/testdata/test_line.go | 295 ---------------------------- 1 file changed, 295 deletions(-) delete mode 100644 src/cmd/cover/testdata/test_line.go diff --git a/src/cmd/cover/testdata/test_line.go b/src/cmd/cover/testdata/test_line.go deleted file mode 100644 index 7cd9590ce48c8..0000000000000 --- a/src/cmd/cover/testdata/test_line.go +++ /dev/null @@ -1,295 +0,0 @@ -// Copyright 2013 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// This program is processed by the cover command, and then testAll is called. -// The test driver in main.go can then compare the coverage statistics with expectation. - -// The word 8 is replaced by the line number in this file. When the file is executed, -// the coverage processing has changed the line numbers, so we can't use runtime.Caller. - -package main - -import _ "unsafe" // for go:linkname - -//go:linkname some_name some_name - -const anything = 1e9 // Just some unlikely value that means "we got here, don't care how often" - -func testAll() { - testSimple() - testBlockRun() - testIf() - testFor() - testRange() - testSwitch() - testTypeSwitch() - testSelect1() - testSelect2() - testPanic() - testEmptySwitches() - testFunctionLiteral() - testGoto() -} - -// The indexes of the counters in testPanic are known to main.go -const panicIndex = 3 - -// This test appears first because the index of its counters is known to main.go -func testPanic() { - defer func() { - recover() - }() - check(43, 1) - panic("should not get next line") - check(45, 0) // this is GoCover.Count[panicIndex] - // The next counter is in testSimple and it will be non-zero. - // If the panic above does not trigger a counter, the test will fail - // because GoCover.Count[panicIndex] will be the one in testSimple. -} - -func testSimple() { - check(52, 1) -} - -func testIf() { - if true { - check(57, 1) - } else { - check(59, 0) - } - if false { - check(62, 0) - } else { - check(64, 1) - } - for i := 0; i < 3; i++ { - if checkVal(67, 3, i) <= 2 { - check(68, 3) - } - if checkVal(70, 3, i) <= 1 { - check(71, 2) - } - if checkVal(73, 3, i) <= 0 { - check(74, 1) - } - } - for i := 0; i < 3; i++ { - if checkVal(78, 3, i) <= 1 { - check(79, 2) - } else { - check(81, 1) - } - } - for i := 0; i < 3; i++ { - if checkVal(85, 3, i) <= 0 { - check(86, 1) - } else if checkVal(87, 2, i) <= 1 { - check(88, 1) - } else if checkVal(89, 1, i) <= 2 { - check(90, 1) - } else if checkVal(91, 0, i) <= 3 { - check(92, 0) - } - } - if func(a, b int) bool { return a < b }(3, 4) { - check(96, 1) - } -} - -func testFor() { - for i := 0; i < 10; func() { i++; check(101, 10) }() { - check(102, 10) - } -} - -func testRange() { - for _, f := range []func(){ - func() { check(108, 1) }, - } { - f() - check(111, 1) - } -} - -func testBlockRun() { - check(116, 1) - { - check(118, 1) - } - { - check(121, 1) - } - check(123, 1) - { - check(125, 1) - } - { - check(128, 1) - } - check(130, 1) -} - -func testSwitch() { - for i := 0; i < 5; func() { i++; check(134, 5) }() { - switch i { - case 0: - check(137, 1) - case 1: - check(139, 1) - case 2: - check(141, 1) - default: - check(143, 2) - } - } -} - -func testTypeSwitch() { - var x = []interface{}{1, 2.0, "hi"} - for _, v := range x { - switch func() { check(151, 3) }(); v.(type) { - case int: - check(153, 1) - case float64: - check(155, 1) - case string: - check(157, 1) - case complex128: - check(159, 0) - default: - check(161, 0) - } - } -} - -func testSelect1() { - c := make(chan int) - go func() { - for i := 0; i < 1000; i++ { - c <- i - } - }() - for { - select { - case <-c: - check(176, anything) - case <-c: - check(178, anything) - default: - check(180, 1) - return - } - } -} - -func testSelect2() { - c1 := make(chan int, 1000) - c2 := make(chan int, 1000) - for i := 0; i < 1000; i++ { - c1 <- i - c2 <- i - } - for { - select { - case <-c1: - check(196, 1000) - case <-c2: - check(198, 1000) - default: - check(200, 1) - return - } - } -} - -// Empty control statements created syntax errors. This function -// is here just to be sure that those are handled correctly now. -func testEmptySwitches() { - check(209, 1) - switch 3 { - } - check(212, 1) - switch i := (interface{})(3).(int); i { - } - check(215, 1) - c := make(chan int) - go func() { - check(218, 1) - c <- 1 - select {} - }() - <-c - check(223, 1) -} - -func testFunctionLiteral() { - a := func(f func()) error { - f() - f() - return nil - } - - b := func(f func()) bool { - f() - f() - return true - } - - check(239, 1) - a(func() { - check(241, 2) - }) - - if err := a(func() { - check(245, 2) - }); err != nil { - } - - switch b(func() { - check(250, 2) - }) { - } - - x := 2 - switch x { - case func() int { check(256, 1); return 1 }(): - check(257, 0) - panic("2=1") - case func() int { check(259, 1); return 2 }(): - check(260, 1) - case func() int { check(261, 0); return 3 }(): - check(262, 0) - panic("2=3") - } -} - -func testGoto() { - for i := 0; i < 2; i++ { - if i == 0 { - goto Label - } - check(272, 1) - Label: - check(274, 2) - } - // Now test that we don't inject empty statements - // between a label and a loop. -loop: - for { - check(280, 1) - break loop - } -} - -// This comment didn't appear in generated go code. -func haha() { - // Needed for cover to add counter increment here. - _ = 42 -} - -// Some someFunction. -// -//go:nosplit -func someFunction() { -} From 943c8f8d8a8e023fc012ed2b9e2f3cb62f548919 Mon Sep 17 00:00:00 2001 From: Felix Kollmann Date: Wed, 28 Mar 2018 14:25:13 +0200 Subject: [PATCH 09/11] os: improved code based on review feedback --- .../syscall/windows/symlink_windows.go | 2 +- src/os/os_windows_test.go | 74 +++---------------- 2 files changed, 11 insertions(+), 65 deletions(-) diff --git a/src/internal/syscall/windows/symlink_windows.go b/src/internal/syscall/windows/symlink_windows.go index 59844d41157b3..f2bcd4c4d0cf2 100644 --- a/src/internal/syscall/windows/symlink_windows.go +++ b/src/internal/syscall/windows/symlink_windows.go @@ -1,4 +1,4 @@ -// Copyright 2016 The Go Authors. All rights reserved. +// Copyright 2018 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. diff --git a/src/os/os_windows_test.go b/src/os/os_windows_test.go index cf795a37858f5..191dd3abe1a8a 100644 --- a/src/os/os_windows_test.go +++ b/src/os/os_windows_test.go @@ -12,7 +12,6 @@ import ( "internal/testenv" "io" "io/ioutil" - "math/rand" "os" osexec "os/exec" "path/filepath" @@ -22,7 +21,6 @@ import ( "strings" "syscall" "testing" - "time" "unicode/utf16" "unsafe" ) @@ -982,84 +980,32 @@ func TestOneDrive(t *testing.T) { testIsDir(t, dir, fi) } -// TestSymlinkWindowsDeveloperModeActive verifies that creating a symbolic link +// 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 TestSymlinkWindowsDeveloperModeActive(t *testing.T) { +func TestSymlinkCreation(t *testing.T) { if !isWindowsDeveloperModeActive() { t.Skip("Windows developer mode is not active") } // create dummy file to symlink - rand.Seed(time.Now().UnixNano()) - - dummyFile := filepath.Join(os.TempDir(), fmt.Sprintf("issue22874.test.%v", 1000000+rand.Int31n(8999999))) - err := ioutil.WriteFile(dummyFile, []byte(""), 0644) - if err != nil { - t.Fatalf("Failed to create dummy file: %v", err) - } - - defer os.Remove(dummyFile) - - // create the symlink - linkFile := fmt.Sprintf("%v.link", dummyFile) - err = os.Symlink(dummyFile, linkFile) + temp, err := ioutil.TempDir("", "TestSymlinkCreation") if err != nil { - // only the ERROR_PRIVILEGE_NOT_HELD error is allowed - if errLink, ok := err.(*os.LinkError); ok { - if errNo, ok := errLink.Err.(syscall.Errno); ok { - - if errNo == syscall.ERROR_PRIVILEGE_NOT_HELD { - t.Fatalf("Windows developer mode is active, but creating symlink failed with ERROR_PRIVILEGE_NOT_HELD anyway: %v", err) - } - } - } - - t.Fatalf("Failed to create symlink: %v", err) - } - // remove the link. don't care for any errors - os.Remove(linkFile) -} - -// TestSymlinkWindowsDeveloperModeInactive verifies that creating a symbolic link -// will fail with the correct error message if developer mode is inactive. -// This test also checks for backward compatibility. -func TestSymlinkWindowsDeveloperModeInactive(t *testing.T) { - if isWindowsDeveloperModeActive() { - t.Skip("Windows developer mode is active") + t.Fatal(err) } + defer os.RemoveAll(temp) - // create dummy file to symlink - rand.Seed(time.Now().UnixNano()) - - dummyFile := filepath.Join(os.TempDir(), fmt.Sprintf("issue22874.test.%v", 1000000+rand.Int31n(8999999))) - err := ioutil.WriteFile(dummyFile, []byte(""), 0644) + dummyFile := filepath.Join(temp, "file") + err = ioutil.WriteFile(dummyFile, []byte(""), 0644) if err != nil { t.Fatalf("Failed to create dummy file: %v", err) } - defer os.Remove(dummyFile) - - // create the symlink - linkFile := fmt.Sprintf("%v.link", dummyFile) + linkFile := filepath.Join(temp, "link") err = os.Symlink(dummyFile, linkFile) - if err == nil { - os.Remove(linkFile) - t.Fatal("Creating the symlink should have failed") - } - - // only the ERROR_PRIVILEGE_NOT_HELD error is allowed - if errLink, ok := err.(*os.LinkError); ok { - if errNo, ok := errLink.Err.(syscall.Errno); ok { - - if errNo == syscall.ERROR_PRIVILEGE_NOT_HELD { - // this error is expected - return - } - } + if err != nil { + t.Fatalf("Failed to create symlink: %v", err) } - - t.Fatalf("Creating symlink failed with unexpected error: %v", err) } // isWindowsDeveloperModeActive checks whether or not the developer mode is active on Windows 10. From bab252e9c444cbbcb83e51b8b4bab0c92e3161a5 Mon Sep 17 00:00:00 2001 From: Felix Kollmann Date: Sat, 7 Apr 2018 00:27:25 +0200 Subject: [PATCH 10/11] os: improved code based on review feedback --- src/os/file_windows.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/os/file_windows.go b/src/os/file_windows.go index 6154fa35bb9a8..a17c6e2ac3374 100644 --- a/src/os/file_windows.go +++ b/src/os/file_windows.go @@ -379,7 +379,7 @@ func Symlink(oldname, newname string) error { } err = syscall.CreateSymbolicLink(n, o, flags) - if err == windows.ERROR_INVALID_PARAMETER { + 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 From 3ba7abcc96ee02837fbfd65c044326c2f1923020 Mon Sep 17 00:00:00 2001 From: Felix Kollmann Date: Wed, 18 Apr 2018 23:04:03 +0200 Subject: [PATCH 11/11] os: improved code based on review feedback --- src/os/os_windows_test.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/os/os_windows_test.go b/src/os/os_windows_test.go index b1b4bd0b5aead..faf0d99992524 100644 --- a/src/os/os_windows_test.go +++ b/src/os/os_windows_test.go @@ -1020,7 +1020,6 @@ func TestSymlinkCreation(t *testing.T) { t.Skip("Windows developer mode is not active") } - // create dummy file to symlink temp, err := ioutil.TempDir("", "TestSymlinkCreation") if err != nil { t.Fatal(err) @@ -1030,13 +1029,13 @@ func TestSymlinkCreation(t *testing.T) { dummyFile := filepath.Join(temp, "file") err = ioutil.WriteFile(dummyFile, []byte(""), 0644) if err != nil { - t.Fatalf("Failed to create dummy file: %v", err) + t.Fatal(err) } linkFile := filepath.Join(temp, "link") err = os.Symlink(dummyFile, linkFile) if err != nil { - t.Fatalf("Failed to create symlink: %v", err) + t.Fatal(err) } }