libgo: update to Go 1.12.2
authorIan Lance Taylor <ian@gcc.gnu.org>
Mon, 8 Apr 2019 18:36:25 +0000 (18:36 +0000)
committerIan Lance Taylor <ian@gcc.gnu.org>
Mon, 8 Apr 2019 18:36:25 +0000 (18:36 +0000)
    Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/170706

From-SVN: r270214

21 files changed:
gcc/go/gofrontend/MERGE
libgo/MERGE
libgo/VERSION
libgo/go/cmd/go/internal/load/pkg.go
libgo/go/cmd/go/internal/load/test.go
libgo/go/cmd/go/internal/test/test.go
libgo/go/cmd/go/internal/work/build.go
libgo/go/cmd/go/internal/work/exec.go
libgo/go/cmd/go/testdata/mod/rsc.io_fortune_v2_v2.0.0.txt
libgo/go/cmd/go/testdata/script/cover_pkgall_multiple_mains.txt [new file with mode: 0644]
libgo/go/internal/poll/fd_windows.go
libgo/go/net/http/httputil/reverseproxy.go
libgo/go/net/http/httputil/reverseproxy_test.go
libgo/go/net/lookup.go
libgo/go/net/lookup_test.go
libgo/go/os/path.go
libgo/go/os/path_unix.go
libgo/go/os/removeall_at.go
libgo/go/os/removeall_test.go
libgo/go/runtime/runtime-lldb_test.go
libgo/go/syscall/security_windows.go

index 64844411c8c01d705c6733ca021b9c2990bfe3ae..1e4da036b8f331f8e01f56c1def8e0840cda9db8 100644 (file)
@@ -1,4 +1,4 @@
-392e9b3da473070f24dbe6c12c282a0e06e73b54
+a69f7c05f1880bb90544fb0c3577109cb1d7f3ab
 
 The first line of this file holds the git revision number of the last
 merge done from the gofrontend repository.
index d456361af0b159bac11ea8947d434308c0615ca7..37b7480a6716bf92b1efef8db95304e1c997a034 100644 (file)
@@ -1,4 +1,4 @@
-0380c9ad38843d523d9c9804fe300cb7edd7cd3c
+ac02fdec7cd16ea8d3de1fc33def9cfabec5170d
 
 The first line of this file holds the git revision number of the
 last merge done from the master library sources.
index f325447817bc7d75c1be02cc1546a70673304f0d..b9c64397147ebc5dfbbde432a6bb43f9e7448f8b 100644 (file)
@@ -1 +1 @@
-go1.12.1
+go1.12.2
index 543250e86c1c89a045fb791676ba8f633d4c448b..5e511dbdbd3623392a81ea87d0e7ca6dae3d278c 100644 (file)
@@ -1184,6 +1184,36 @@ var cgoSyscallExclude = map[string]bool{
 
 var foldPath = make(map[string]string)
 
+// DefaultExecName returns the default executable name
+// for a package with the import path importPath.
+//
+// The default executable name is the last element of the import path.
+// In module-aware mode, an additional rule is used. If the last element
+// is a vN path element specifying the major version, then the second last
+// element of the import path is used instead.
+func DefaultExecName(importPath string) string {
+       _, elem := pathpkg.Split(importPath)
+       if cfg.ModulesEnabled {
+               // If this is example.com/mycmd/v2, it's more useful to install it as mycmd than as v2.
+               // See golang.org/issue/24667.
+               isVersion := func(v string) bool {
+                       if len(v) < 2 || v[0] != 'v' || v[1] < '1' || '9' < v[1] {
+                               return false
+                       }
+                       for i := 2; i < len(v); i++ {
+                               if c := v[i]; c < '0' || '9' < c {
+                                       return false
+                               }
+                       }
+                       return true
+               }
+               if isVersion(elem) {
+                       _, elem = pathpkg.Split(pathpkg.Dir(importPath))
+               }
+       }
+       return elem
+}
+
 // load populates p using information from bp, err, which should
 // be the result of calling build.Context.Import.
 func (p *Package) load(stk *ImportStack, bp *build.Package, err error) {
@@ -1226,7 +1256,7 @@ func (p *Package) load(stk *ImportStack, bp *build.Package, err error) {
                }
                _, elem := filepath.Split(p.Dir)
                if cfg.ModulesEnabled {
-                       // NOTE(rsc): Using p.ImportPath instead of p.Dir
+                       // NOTE(rsc,dmitshur): Using p.ImportPath instead of p.Dir
                        // makes sure we install a package in the root of a
                        // cached module directory as that package name
                        // not name@v1.2.3.
@@ -1235,26 +1265,9 @@ func (p *Package) load(stk *ImportStack, bp *build.Package, err error) {
                        // even for non-module-enabled code,
                        // but I'm not brave enough to change the
                        // non-module behavior this late in the
-                       // release cycle. Maybe for Go 1.12.
+                       // release cycle. Can be done for Go 1.13.
                        // See golang.org/issue/26869.
-                       _, elem = pathpkg.Split(p.ImportPath)
-
-                       // If this is example.com/mycmd/v2, it's more useful to install it as mycmd than as v2.
-                       // See golang.org/issue/24667.
-                       isVersion := func(v string) bool {
-                               if len(v) < 2 || v[0] != 'v' || v[1] < '1' || '9' < v[1] {
-                                       return false
-                               }
-                               for i := 2; i < len(v); i++ {
-                                       if c := v[i]; c < '0' || '9' < c {
-                                               return false
-                                       }
-                               }
-                               return true
-                       }
-                       if isVersion(elem) {
-                               _, elem = pathpkg.Split(pathpkg.Dir(p.ImportPath))
-                       }
+                       elem = DefaultExecName(p.ImportPath)
                }
                full := cfg.BuildContext.GOOS + "_" + cfg.BuildContext.GOARCH + "/" + elem
                if cfg.BuildContext.GOOS != base.ToolGOOS || cfg.BuildContext.GOARCH != base.ToolGOARCH {
index c0e06676df1eb22403f2bf53fc71f48c8468a87a..7385c4e5030ec1b35ffc4603c7507d96701c49f2 100644 (file)
@@ -268,17 +268,8 @@ func GetTestPackagesFor(p *Package, cover *TestCover) (pmain, ptest, pxtest *Pac
        pmain.Imports = pmain.Imports[:w]
        pmain.Internal.RawImports = str.StringList(pmain.Imports)
 
-       if ptest != p {
-               // We have made modifications to the package p being tested
-               // and are rebuilding p (as ptest).
-               // Arrange to rebuild all packages q such that
-               // the test depends on q and q depends on p.
-               // This makes sure that q sees the modifications to p.
-               // Strictly speaking, the rebuild is only necessary if the
-               // modifications to p change its export metadata, but
-               // determining that is a bit tricky, so we rebuild always.
-               recompileForTest(pmain, p, ptest, pxtest)
-       }
+       // Replace pmain's transitive dependencies with test copies, as necessary.
+       recompileForTest(pmain, p, ptest, pxtest)
 
        // Should we apply coverage analysis locally,
        // only for this package and only for this test?
@@ -325,6 +316,14 @@ Search:
        return stk
 }
 
+// recompileForTest copies and replaces certain packages in pmain's dependency
+// graph. This is necessary for two reasons. First, if ptest is different than
+// preal, packages that import the package under test should get ptest instead
+// of preal. This is particularly important if pxtest depends on functionality
+// exposed in test sources in ptest. Second, if there is a main package
+// (other than pmain) anywhere, we need to clear p.Internal.BuildInfo in
+// the test copy to prevent link conflicts. This may happen if both -coverpkg
+// and the command line patterns include multiple main packages.
 func recompileForTest(pmain, preal, ptest, pxtest *Package) {
        // The "test copy" of preal is ptest.
        // For each package that depends on preal, make a "test copy"
@@ -367,7 +366,7 @@ func recompileForTest(pmain, preal, ptest, pxtest *Package) {
 
                // Don't compile build info from a main package. This can happen
                // if -coverpkg patterns include main packages, since those packages
-               // are imported by pmain.
+               // are imported by pmain. See golang.org/issue/30907.
                if p.Internal.BuildInfo != "" && p != pmain {
                        split()
                }
index 332f31244154165d50cfd3ad82c12e4595e2c480..b72eace55ab13646d53dbce64e8af2726ccc4400 100644 (file)
@@ -805,7 +805,7 @@ func builderTest(b *work.Builder, p *load.Package) (buildAction, runAction, prin
        if p.ImportPath == "command-line-arguments" {
                elem = p.Name
        } else {
-               _, elem = path.Split(p.ImportPath)
+               elem = load.DefaultExecName(p.ImportPath)
        }
        testBinary := elem + ".test"
 
index 145b87513a9166aecb9d9c7b7efd690425f3b1b3..ed66df22c33b4714b1251ebfcca9c0ded7453a29 100644 (file)
@@ -10,7 +10,6 @@ import (
        "go/build"
        "os"
        "os/exec"
-       "path"
        "path/filepath"
        "runtime"
        "strings"
@@ -285,7 +284,7 @@ func runBuild(cmd *base.Command, args []string) {
        pkgs := load.PackagesForBuild(args)
 
        if len(pkgs) == 1 && pkgs[0].Name == "main" && cfg.BuildO == "" {
-               _, cfg.BuildO = path.Split(pkgs[0].ImportPath)
+               cfg.BuildO = load.DefaultExecName(pkgs[0].ImportPath)
                cfg.BuildO += cfg.ExeSuffix
        }
 
@@ -518,7 +517,7 @@ func InstallPackages(patterns []string, pkgs []*load.Package) {
        if len(patterns) == 0 && len(pkgs) == 1 && pkgs[0].Name == "main" {
                // Compute file 'go build' would have created.
                // If it exists and is an executable file, remove it.
-               _, targ := filepath.Split(pkgs[0].ImportPath)
+               targ := load.DefaultExecName(pkgs[0].ImportPath)
                targ += cfg.ExeSuffix
                if filepath.Join(pkgs[0].Dir, targ) != pkgs[0].Target { // maybe $GOBIN is the current directory
                        fi, err := os.Stat(targ)
index fc052b3e45a2c13f64f510c6c66796741ba5a633..530272466141ce980c4795dd85d0cb4c72d0abd7 100644 (file)
@@ -214,6 +214,7 @@ func (b *Builder) buildActionID(a *Action) cache.ActionID {
        if p.Internal.CoverMode != "" {
                fmt.Fprintf(h, "cover %q %q\n", p.Internal.CoverMode, b.toolID("cover"))
        }
+       fmt.Fprintf(h, "modinfo %q\n", p.Internal.BuildInfo)
 
        // Configuration specific to compiler toolchain.
        switch cfg.BuildToolchainName {
index cfa91f08a5d84ba07ffd96eef8580a43c455899b..3acd6379311f643194e45a712464b4af4aed5fa6 100644 (file)
@@ -13,3 +13,9 @@ import "rsc.io/quote"
 func main() {
        println(quote.Hello())
 }
+-- fortune_test.go --
+package main
+
+import "testing"
+
+func TestFortuneV2(t *testing.T) {}
diff --git a/libgo/go/cmd/go/testdata/script/cover_pkgall_multiple_mains.txt b/libgo/go/cmd/go/testdata/script/cover_pkgall_multiple_mains.txt
new file mode 100644 (file)
index 0000000..ab7cd66
--- /dev/null
@@ -0,0 +1,43 @@
+# This test checks that multiple main packages can be tested
+# with -coverpkg=all without duplicate symbol errors.
+# Verifies golang.org/issue/30374.
+
+env GO111MODULE=on
+
+[short] skip
+
+go test -coverpkg=all ./...
+
+-- go.mod --
+module example.com/cov
+
+-- mainonly/mainonly.go --
+package main
+
+func main() {}
+
+-- mainwithtest/mainwithtest.go --
+package main
+
+func main() {}
+
+func Foo() {}
+
+-- mainwithtest/mainwithtest_test.go --
+package main
+
+import "testing"
+
+func TestFoo(t *testing.T) {
+  Foo()
+}
+
+-- xtest/x.go --
+package x
+
+-- xtest/x_test.go --
+package x_test
+
+import "testing"
+
+func TestX(t *testing.T) {}
index 19d9a12dad83b9b9e1d388c113328fc0dc409bea..f860b82c97489e4e30c8cb3e28ae20f9ca8aed4f 100644 (file)
@@ -660,6 +660,10 @@ func (fd *FD) Write(buf []byte) (int, error) {
                return 0, err
        }
        defer fd.writeUnlock()
+       if fd.isFile || fd.isDir || fd.isConsole {
+               fd.l.Lock()
+               defer fd.l.Unlock()
+       }
 
        ntotal := 0
        for len(buf) > 0 {
@@ -670,8 +674,6 @@ func (fd *FD) Write(buf []byte) (int, error) {
                var n int
                var err error
                if fd.isFile || fd.isDir || fd.isConsole {
-                       fd.l.Lock()
-                       defer fd.l.Unlock()
                        if fd.isConsole {
                                n, err = fd.writeConsole(b)
                        } else {
index 4e10bf399711b5f54e9cb985f0049738db46a97b..4b165d65a6aff44f352d9852bd64dd69421fd2b2 100644 (file)
@@ -389,6 +389,11 @@ func (p *ReverseProxy) copyResponse(dst io.Writer, src io.Reader, flushInterval
                                latency: flushInterval,
                        }
                        defer mlw.stop()
+
+                       // set up initial timer so headers get flushed even if body writes are delayed
+                       mlw.flushPending = true
+                       mlw.t = time.AfterFunc(flushInterval, mlw.delayedFlush)
+
                        dst = mlw
                }
        }
index 5edefa08e55a3c1a17bda4d4957b12dbc4bfeb91..367ba73ae2497919748186a3ec886a0f17b8eef7 100644 (file)
@@ -9,6 +9,7 @@ package httputil
 import (
        "bufio"
        "bytes"
+       "context"
        "errors"
        "fmt"
        "io"
@@ -317,6 +318,47 @@ func TestReverseProxyFlushInterval(t *testing.T) {
        }
 }
 
+func TestReverseProxyFlushIntervalHeaders(t *testing.T) {
+       const expected = "hi"
+       stopCh := make(chan struct{})
+       backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+               w.Header().Add("MyHeader", expected)
+               w.WriteHeader(200)
+               w.(http.Flusher).Flush()
+               <-stopCh
+       }))
+       defer backend.Close()
+       defer close(stopCh)
+
+       backendURL, err := url.Parse(backend.URL)
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       proxyHandler := NewSingleHostReverseProxy(backendURL)
+       proxyHandler.FlushInterval = time.Microsecond
+
+       frontend := httptest.NewServer(proxyHandler)
+       defer frontend.Close()
+
+       req, _ := http.NewRequest("GET", frontend.URL, nil)
+       req.Close = true
+
+       ctx, cancel := context.WithTimeout(req.Context(), 10*time.Second)
+       defer cancel()
+       req = req.WithContext(ctx)
+
+       res, err := frontend.Client().Do(req)
+       if err != nil {
+               t.Fatalf("Get: %v", err)
+       }
+       defer res.Body.Close()
+
+       if res.Header.Get("MyHeader") != expected {
+               t.Errorf("got header %q; expected %q", res.Header.Get("MyHeader"), expected)
+       }
+}
+
 func TestReverseProxyCancelation(t *testing.T) {
        const backendResponse = "I am the backend"
 
index e10889331e4f5ef5c0bd189cad5eec3ab14e65d1..08e8d013855f4f9123544508be2fe2042a1cadf3 100644 (file)
@@ -262,8 +262,9 @@ func (r *Resolver) lookupIPAddr(ctx context.Context, network, host string) ([]IP
        // only the values in context. See Issue 28600.
        lookupGroupCtx, lookupGroupCancel := context.WithCancel(withUnexpiredValuesPreserved(ctx))
 
+       lookupKey := network + "\000" + host
        dnsWaitGroup.Add(1)
-       ch, called := r.getLookupGroup().DoChan(host, func() (interface{}, error) {
+       ch, called := r.getLookupGroup().DoChan(lookupKey, func() (interface{}, error) {
                defer dnsWaitGroup.Done()
                return testHookLookupIP(lookupGroupCtx, resolverFunc, network, host)
        })
@@ -280,7 +281,7 @@ func (r *Resolver) lookupIPAddr(ctx context.Context, network, host string) ([]IP
                // let the lookup continue uncanceled, and let later
                // lookups with the same key share the result.
                // See issues 8602, 20703, 22724.
-               if r.getLookupGroup().ForgetUnshared(host) {
+               if r.getLookupGroup().ForgetUnshared(lookupKey) {
                        lookupGroupCancel()
                } else {
                        go func() {
index 85bcb2b8960b20a207507fac7ed64cfd3db5fef6..28a895e15d13c03cf11d0cfd335f6ad5f8ef8546 100644 (file)
@@ -16,6 +16,7 @@ import (
        "sort"
        "strings"
        "sync"
+       "sync/atomic"
        "testing"
        "time"
 )
@@ -253,14 +254,11 @@ func TestLookupGmailTXT(t *testing.T) {
        }
 }
 
-var lookupGooglePublicDNSAddrTests = []struct {
-       addr, name string
-}{
-       {"8.8.8.8", ".google.com."},
-       {"8.8.4.4", ".google.com."},
-
-       {"2001:4860:4860::8888", ".google.com."},
-       {"2001:4860:4860::8844", ".google.com."},
+var lookupGooglePublicDNSAddrTests = []string{
+       "8.8.8.8",
+       "8.8.4.4",
+       "2001:4860:4860::8888",
+       "2001:4860:4860::8844",
 }
 
 func TestLookupGooglePublicDNSAddr(t *testing.T) {
@@ -272,8 +270,8 @@ func TestLookupGooglePublicDNSAddr(t *testing.T) {
 
        defer dnsWaitGroup.Wait()
 
-       for _, tt := range lookupGooglePublicDNSAddrTests {
-               names, err := LookupAddr(tt.addr)
+       for _, ip := range lookupGooglePublicDNSAddrTests {
+               names, err := LookupAddr(ip)
                if err != nil {
                        t.Fatal(err)
                }
@@ -281,8 +279,8 @@ func TestLookupGooglePublicDNSAddr(t *testing.T) {
                        t.Error("got no record")
                }
                for _, name := range names {
-                       if !strings.HasSuffix(name, tt.name) {
-                               t.Errorf("got %s; want a record containing %s", name, tt.name)
+                       if !strings.HasSuffix(name, ".google.com.") && !strings.HasSuffix(name, ".google.") {
+                               t.Errorf("got %q; want a record ending in .google.com. or .google.", name)
                        }
                }
        }
@@ -658,8 +656,8 @@ func testDots(t *testing.T, mode string) {
                t.Errorf("LookupAddr(8.8.8.8): %v (mode=%v)", err, mode)
        } else {
                for _, name := range names {
-                       if !strings.HasSuffix(name, ".google.com.") {
-                               t.Errorf("LookupAddr(8.8.8.8) = %v, want names ending in .google.com. with trailing dot (mode=%v)", names, mode)
+                       if !strings.HasSuffix(name, ".google.com.") && !strings.HasSuffix(name, ".google.") {
+                               t.Errorf("LookupAddr(8.8.8.8) = %v, want names ending in .google.com or .google with trailing dot (mode=%v)", names, mode)
                                break
                        }
                }
@@ -1096,6 +1094,69 @@ func TestLookupIPAddrPreservesContextValues(t *testing.T) {
        }
 }
 
+// Issue 30521: The lookup group should call the resolver for each network.
+func TestLookupIPAddrConcurrentCallsForNetworks(t *testing.T) {
+       origTestHookLookupIP := testHookLookupIP
+       defer func() { testHookLookupIP = origTestHookLookupIP }()
+
+       queries := [][]string{
+               {"udp", "golang.org"},
+               {"udp4", "golang.org"},
+               {"udp6", "golang.org"},
+               {"udp", "golang.org"},
+               {"udp", "golang.org"},
+       }
+       results := map[[2]string][]IPAddr{
+               {"udp", "golang.org"}: {
+                       {IP: IPv4(127, 0, 0, 1)},
+                       {IP: IPv6loopback},
+               },
+               {"udp4", "golang.org"}: {
+                       {IP: IPv4(127, 0, 0, 1)},
+               },
+               {"udp6", "golang.org"}: {
+                       {IP: IPv6loopback},
+               },
+       }
+       calls := int32(0)
+       waitCh := make(chan struct{})
+       testHookLookupIP = func(ctx context.Context, fn func(context.Context, string, string) ([]IPAddr, error), network, host string) ([]IPAddr, error) {
+               // We'll block until this is called one time for each different
+               // expected result. This will ensure that the lookup group would wait
+               // for the existing call if it was to be reused.
+               if atomic.AddInt32(&calls, 1) == int32(len(results)) {
+                       close(waitCh)
+               }
+               select {
+               case <-waitCh:
+               case <-ctx.Done():
+                       return nil, ctx.Err()
+               }
+               return results[[2]string{network, host}], nil
+       }
+
+       ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+       defer cancel()
+       wg := sync.WaitGroup{}
+       for _, q := range queries {
+               network := q[0]
+               host := q[1]
+               wg.Add(1)
+               go func() {
+                       defer wg.Done()
+                       gotIPs, err := DefaultResolver.lookupIPAddr(ctx, network, host)
+                       if err != nil {
+                               t.Errorf("lookupIPAddr(%v, %v): unexpected error: %v", network, host, err)
+                       }
+                       wantIPs := results[[2]string{network, host}]
+                       if !reflect.DeepEqual(gotIPs, wantIPs) {
+                               t.Errorf("lookupIPAddr(%v, %v): mismatched IPAddr results\n\tGot: %v\n\tWant: %v", network, host, gotIPs, wantIPs)
+                       }
+               }()
+       }
+       wg.Wait()
+}
+
 func TestWithUnexpiredValuesPreserved(t *testing.T) {
        ctx, cancel := context.WithCancel(context.Background())
 
index 104b7ceaf7dad37e58d852b954036ed9a790ba11..ba43ea352547b9e4018df73c55b2b8d9a6f9bc90 100644 (file)
@@ -62,6 +62,7 @@ func MkdirAll(path string, perm FileMode) error {
 // It removes everything it can but returns the first error
 // it encounters. If the path does not exist, RemoveAll
 // returns nil (no error).
+// If there is an error, it will be of type *PathError.
 func RemoveAll(path string) error {
        return removeAll(path)
 }
index e55301f9765d6b6e8df6074a29c0374935ec74ad..4864989fb5b5f70c1f5ebbd36dda3d18b31e5a3b 100644 (file)
@@ -51,7 +51,7 @@ func splitPath(path string) (string, string) {
        // Remove leading directory path
        for i--; i >= 0; i-- {
                if path[i] == '/' {
-                       dirname = path[:i+1]
+                       dirname = path[:i]
                        basename = path[i+1:]
                        break
                }
index 512a891017dc34225f3e39f05c5a8f8de75cb64e..6fdd7e828d6b8d7859c7a6bbef8549fc7b31b861 100644 (file)
@@ -46,13 +46,20 @@ func removeAll(path string) error {
        }
        defer parent.Close()
 
-       return removeAllFrom(parent, base)
+       if err := removeAllFrom(parent, base); err != nil {
+               if pathErr, ok := err.(*PathError); ok {
+                       pathErr.Path = parentDir + string(PathSeparator) + pathErr.Path
+                       err = pathErr
+               }
+               return err
+       }
+       return nil
 }
 
-func removeAllFrom(parent *File, path string) error {
+func removeAllFrom(parent *File, base string) error {
        parentFd := int(parent.Fd())
        // Simple case: if Unlink (aka remove) works, we're done.
-       err := unix.Unlinkat(parentFd, path, 0)
+       err := unix.Unlinkat(parentFd, base, 0)
        if err == nil || IsNotExist(err) {
                return nil
        }
@@ -64,21 +71,21 @@ func removeAllFrom(parent *File, path string) error {
        // whose contents need to be removed.
        // Otherwise just return the error.
        if err != syscall.EISDIR && err != syscall.EPERM && err != syscall.EACCES {
-               return err
+               return &PathError{"unlinkat", base, err}
        }
 
        // Is this a directory we need to recurse into?
        var statInfo syscall.Stat_t
-       statErr := unix.Fstatat(parentFd, path, &statInfo, unix.AT_SYMLINK_NOFOLLOW)
+       statErr := unix.Fstatat(parentFd, base, &statInfo, unix.AT_SYMLINK_NOFOLLOW)
        if statErr != nil {
                if IsNotExist(statErr) {
                        return nil
                }
-               return statErr
+               return &PathError{"fstatat", base, statErr}
        }
        if statInfo.Mode&syscall.S_IFMT != syscall.S_IFDIR {
-               // Not a directory; return the error from the Remove.
-               return err
+               // Not a directory; return the error from the unix.Unlinkat.
+               return &PathError{"unlinkat", base, err}
        }
 
        // Remove the directory's entries.
@@ -87,12 +94,12 @@ func removeAllFrom(parent *File, path string) error {
                const request = 1024
 
                // Open the directory to recurse into
-               file, err := openFdAt(parentFd, path)
+               file, err := openFdAt(parentFd, base)
                if err != nil {
                        if IsNotExist(err) {
                                return nil
                        }
-                       recurseErr = err
+                       recurseErr = &PathError{"openfdat", base, err}
                        break
                }
 
@@ -103,12 +110,15 @@ func removeAllFrom(parent *File, path string) error {
                        if IsNotExist(readErr) {
                                return nil
                        }
-                       return readErr
+                       return &PathError{"readdirnames", base, readErr}
                }
 
                for _, name := range names {
                        err := removeAllFrom(file, name)
                        if err != nil {
+                               if pathErr, ok := err.(*PathError); ok {
+                                       pathErr.Path = base + string(PathSeparator) + pathErr.Path
+                               }
                                recurseErr = err
                        }
                }
@@ -127,7 +137,7 @@ func removeAllFrom(parent *File, path string) error {
        }
 
        // Remove the directory itself.
-       unlinkError := unix.Unlinkat(parentFd, path, unix.AT_REMOVEDIR)
+       unlinkError := unix.Unlinkat(parentFd, base, unix.AT_REMOVEDIR)
        if unlinkError == nil || IsNotExist(unlinkError) {
                return nil
        }
@@ -135,7 +145,7 @@ func removeAllFrom(parent *File, path string) error {
        if recurseErr != nil {
                return recurseErr
        }
-       return unlinkError
+       return &PathError{"unlinkat", base, unlinkError}
 }
 
 // openFdAt opens path relative to the directory in fd.
@@ -157,7 +167,7 @@ func openFdAt(dirfd int, name string) (*File, error) {
                        continue
                }
 
-               return nil, &PathError{"openat", name, e}
+               return nil, e
        }
 
        if !supportsCloseOnExec {
index 21371d8776a68ccf54f739d1fb654b7dd2a90034..945a38e8e06df37e1dc3bb5071fc75a071e2a6ac 100644 (file)
@@ -294,7 +294,7 @@ func TestRemoveReadOnlyDir(t *testing.T) {
 }
 
 // Issue #29983.
-func TestRemoveAllButReadOnly(t *testing.T) {
+func TestRemoveAllButReadOnlyAndPathError(t *testing.T) {
        switch runtime.GOOS {
        case "nacl", "js", "windows":
                t.Skipf("skipping test on %s", runtime.GOOS)
@@ -355,10 +355,21 @@ func TestRemoveAllButReadOnly(t *testing.T) {
                defer Chmod(d, 0777)
        }
 
-       if err := RemoveAll(tempDir); err == nil {
+       err = RemoveAll(tempDir)
+       if err == nil {
                t.Fatal("RemoveAll succeeded unexpectedly")
        }
 
+       // The error should be of type *PathError.
+       // see issue 30491 for details.
+       if pathErr, ok := err.(*PathError); ok {
+               if g, w := pathErr.Path, filepath.Join(tempDir, "b", "y"); g != w {
+                       t.Errorf("got %q, expected pathErr.path %q", g, w)
+               }
+       } else {
+               t.Errorf("got %T, expected *os.PathError", err)
+       }
+
        for _, dir := range dirs {
                _, err := Stat(filepath.Join(tempDir, dir))
                if inReadonly(dir) {
index fe3a0eb90d8c334f006a041cfae5802bafb716f6..08d6a34f50c669a3ce4e13307a09915ab21eed84 100644 (file)
@@ -139,6 +139,7 @@ func TestLldbPython(t *testing.T) {
        if final := os.Getenv("GOROOT_FINAL"); final != "" && runtime.GOROOT() != final {
                t.Skip("gdb test can fail with GOROOT_FINAL pending")
        }
+       testenv.SkipFlaky(t, 31188)
 
        checkLldbPython(t)
 
index ae8b3a17bfb0f77a000726687994b3ab065e6156..db80d98a084d019832c52cce5a77126663951ecc 100644 (file)
@@ -290,6 +290,7 @@ type Tokenprimarygroup struct {
 //sys  OpenProcessToken(h Handle, access uint32, token *Token) (err error) = advapi32.OpenProcessToken
 //sys  GetTokenInformation(t Token, infoClass uint32, info *byte, infoLen uint32, returnedLen *uint32) (err error) = advapi32.GetTokenInformation
 //sys  GetUserProfileDirectory(t Token, dir *uint16, dirLen *uint32) (err error) = userenv.GetUserProfileDirectoryW
+//sys  getSystemDirectory(dir *uint16, dirLen uint32) (len uint32, err error) = kernel32.GetSystemDirectoryW
 
 // An access token contains the security information for a logon session.
 // The system creates an access token when a user logs on, and every