diff options
author | Ian Lance Taylor <iant@golang.org> | 2022-02-11 14:53:56 -0800 |
---|---|---|
committer | Ian Lance Taylor <iant@golang.org> | 2022-02-11 15:01:19 -0800 |
commit | 8dc2499aa62f768c6395c9754b8cabc1ce25c494 (patch) | |
tree | 43d7fd2bbfd7ad8c9625a718a5e8718889351994 /libgo/go/os | |
parent | 9a56779dbc4e2d9c15be8d31e36f2f59be7331a8 (diff) | |
download | gcc-8dc2499aa62f768c6395c9754b8cabc1ce25c494.tar.gz |
libgo: update to Go1.18beta2
gotools/
* Makefile.am (go_cmd_cgo_files): Add ast_go118.go
(check-go-tool): Copy golang.org/x/tools directories.
* Makefile.in: Regenerate.
Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/384695
Diffstat (limited to 'libgo/go/os')
87 files changed, 632 insertions, 507 deletions
diff --git a/libgo/go/os/env_test.go b/libgo/go/os/env_test.go index 4b860157b4c..f8d56ef8e02 100644 --- a/libgo/go/os/env_test.go +++ b/libgo/go/os/env_test.go @@ -66,7 +66,7 @@ func TestExpand(t *testing.T) { } } -var global interface{} +var global any func BenchmarkExpand(b *testing.B) { b.Run("noop", func(b *testing.B) { @@ -166,3 +166,39 @@ func TestLookupEnv(t *testing.T) { t.Errorf("smallpox release failed; world remains safe but LookupEnv is broken") } } + +// On Windows, Environ was observed to report keys with a single leading "=". +// Check that they are properly reported by LookupEnv and can be set by SetEnv. +// See https://golang.org/issue/49886. +func TestEnvironConsistency(t *testing.T) { + for _, kv := range Environ() { + i := strings.Index(kv, "=") + if i == 0 { + // We observe in practice keys with a single leading "=" on Windows. + // TODO(#49886): Should we consume only the first leading "=" as part + // of the key, or parse through arbitrarily many of them until a non-=, + // or try each possible key/value boundary until LookupEnv succeeds? + i = strings.Index(kv[1:], "=") + 1 + } + if i < 0 { + t.Errorf("Environ entry missing '=': %q", kv) + } + + k := kv[:i] + v := kv[i+1:] + v2, ok := LookupEnv(k) + if ok && v == v2 { + t.Logf("LookupEnv(%q) = %q, %t", k, v2, ok) + } else { + t.Errorf("Environ contains %q, but LookupEnv(%q) = %q, %t", kv, k, v2, ok) + } + + // Since k=v is already present in the environment, + // setting it should be a no-op. + if err := Setenv(k, v); err == nil { + t.Logf("Setenv(%q, %q)", k, v) + } else { + t.Errorf("Environ contains %q, but SetEnv(%q, %q) = %q", kv, k, v, err) + } + } +} diff --git a/libgo/go/os/env_unix_test.go b/libgo/go/os/env_unix_test.go index 0336323e8b6..c82a558c6e8 100644 --- a/libgo/go/os/env_unix_test.go +++ b/libgo/go/os/env_unix_test.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || hurd || linux || netbsd || openbsd || solaris -// +build aix darwin dragonfly freebsd hurd linux netbsd openbsd solaris package os_test diff --git a/libgo/go/os/error_errno.go b/libgo/go/os/error_errno.go index 580e915b73c..c8140461a4d 100644 --- a/libgo/go/os/error_errno.go +++ b/libgo/go/os/error_errno.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !plan9 -// +build !plan9 package os diff --git a/libgo/go/os/error_posix.go b/libgo/go/os/error_posix.go index 7a7c8609e27..eb4f54bc610 100644 --- a/libgo/go/os/error_posix.go +++ b/libgo/go/os/error_posix.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || hurd || (js && wasm) || linux || netbsd || openbsd || solaris || windows -// +build aix darwin dragonfly freebsd hurd js,wasm linux netbsd openbsd solaris windows package os diff --git a/libgo/go/os/error_unix_test.go b/libgo/go/os/error_unix_test.go index 3f419d54702..012e053883f 100644 --- a/libgo/go/os/error_unix_test.go +++ b/libgo/go/os/error_unix_test.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || hurd || (js && wasm) || linux || netbsd || openbsd || solaris -// +build aix darwin dragonfly freebsd hurd js,wasm linux netbsd openbsd solaris package os_test diff --git a/libgo/go/os/error_windows_test.go b/libgo/go/os/error_windows_test.go index aa0c14b7d46..86c8a985bb3 100644 --- a/libgo/go/os/error_windows_test.go +++ b/libgo/go/os/error_windows_test.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build windows -// +build windows package os_test diff --git a/libgo/go/os/exec.go b/libgo/go/os/exec.go index bc75d4dd66c..9eb3166ecb7 100644 --- a/libgo/go/os/exec.go +++ b/libgo/go/os/exec.go @@ -149,6 +149,8 @@ func (p *ProcessState) SystemTime() time.Duration { } // Exited reports whether the program has exited. +// On Unix systems this reports true if the program exited due to calling exit, +// but false if the program terminated due to a signal. func (p *ProcessState) Exited() bool { return p.exited() } @@ -162,7 +164,7 @@ func (p *ProcessState) Success() bool { // Sys returns system-dependent exit information about // the process. Convert it to the appropriate underlying // type, such as syscall.WaitStatus on Unix, to access its contents. -func (p *ProcessState) Sys() interface{} { +func (p *ProcessState) Sys() any { return p.sys() } @@ -171,6 +173,6 @@ func (p *ProcessState) Sys() interface{} { // type, such as *syscall.Rusage on Unix, to access its contents. // (On Unix, *syscall.Rusage matches struct rusage as defined in the // getrusage(2) manual page.) -func (p *ProcessState) SysUsage() interface{} { +func (p *ProcessState) SysUsage() any { return p.sysUsage() } diff --git a/libgo/go/os/exec/exec.go b/libgo/go/os/exec/exec.go index 0c495755116..845b737e28f 100644 --- a/libgo/go/os/exec/exec.go +++ b/libgo/go/os/exec/exec.go @@ -216,7 +216,7 @@ func (c *Cmd) String() string { // interfaceEqual protects against panics from doing equality tests on // two interfaces with non-comparable underlying types. -func interfaceEqual(a, b interface{}) bool { +func interfaceEqual(a, b any) bool { defer func() { recover() }() @@ -748,12 +748,11 @@ func dedupEnvCase(caseInsensitive bool, env []string) []string { out := make([]string, 0, len(env)) saw := make(map[string]int, len(env)) // key => index into out for _, kv := range env { - eq := strings.Index(kv, "=") - if eq < 0 { + k, _, ok := strings.Cut(kv, "=") + if !ok { out = append(out, kv) continue } - k := kv[:eq] if caseInsensitive { k = strings.ToLower(k) } @@ -775,11 +774,10 @@ func addCriticalEnv(env []string) []string { return env } for _, kv := range env { - eq := strings.Index(kv, "=") - if eq < 0 { + k, _, ok := strings.Cut(kv, "=") + if !ok { continue } - k := kv[:eq] if strings.EqualFold(k, "SYSTEMROOT") { // We already have it. return env diff --git a/libgo/go/os/exec/exec_linux_test.go b/libgo/go/os/exec/exec_linux_test.go index 3cfa30ee724..4a37c96e63a 100644 --- a/libgo/go/os/exec/exec_linux_test.go +++ b/libgo/go/os/exec/exec_linux_test.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build linux && cgo -// +build linux,cgo // On systems that use glibc, calling malloc can create a new arena, // and creating a new arena can read /sys/devices/system/cpu/online. diff --git a/libgo/go/os/exec/exec_posix_test.go b/libgo/go/os/exec/exec_posix_test.go index 7b2c0c0c111..fd7fb42d36a 100644 --- a/libgo/go/os/exec/exec_posix_test.go +++ b/libgo/go/os/exec/exec_posix_test.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris package exec_test diff --git a/libgo/go/os/exec/exec_test.go b/libgo/go/os/exec/exec_test.go index d854e0de843..73aa35f1aed 100644 --- a/libgo/go/os/exec/exec_test.go +++ b/libgo/go/os/exec/exec_test.go @@ -21,7 +21,9 @@ import ( "net/http/httptest" "os" "os/exec" + "os/exec/internal/fdtest" "path/filepath" + "reflect" "runtime" "strconv" "strings" @@ -29,15 +31,10 @@ import ( "time" ) -// haveUnexpectedFDs is set at init time to report whether any -// file descriptors were open at program start. +// haveUnexpectedFDs is set at init time to report whether any file descriptors +// were open at program start. var haveUnexpectedFDs bool -// unfinalizedFiles holds files that should not be finalized, -// because that would close the associated file descriptor, -// which we don't want to do. -var unfinalizedFiles []*os.File - func init() { if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" { return @@ -49,21 +46,10 @@ func init() { if poll.IsPollDescriptor(fd) { continue } - // We have no good portable way to check whether an FD is open. - // We use NewFile to create a *os.File, which lets us - // know whether it is open, but then we have to cope with - // the finalizer on the *os.File. - f := os.NewFile(fd, "") - if _, err := f.Stat(); err != nil { - // Close the file to clear the finalizer. - // We expect the Close to fail. - f.Close() - } else { - fmt.Printf("fd %d open at test start\n", fd) + + if fdtest.Exists(fd) { haveUnexpectedFDs = true - // Use a global variable to avoid running - // the finalizer, which would close the FD. - unfinalizedFiles = append(unfinalizedFiles, f) + return } } } @@ -166,12 +152,10 @@ func TestCatGoodAndBadFile(t *testing.T) { if _, ok := err.(*exec.ExitError); !ok { t.Errorf("expected *exec.ExitError from cat combined; got %T: %v", err, err) } - s := string(bs) - sp := strings.SplitN(s, "\n", 2) - if len(sp) != 2 { - t.Fatalf("expected two lines from cat; got %q", s) + errLine, body, ok := strings.Cut(string(bs), "\n") + if !ok { + t.Fatalf("expected two lines from cat; got %q", bs) } - errLine, body := sp[0], sp[1] if !strings.HasPrefix(errLine, "Error: open /bogus/file.foo") { t.Errorf("expected stderr to complain about file; got %q", errLine) } @@ -379,50 +363,21 @@ func TestStdinCloseRace(t *testing.T) { // Issue 5071 func TestPipeLookPathLeak(t *testing.T) { - // If we are reading from /proc/self/fd we (should) get an exact result. - tolerance := 0 - - // Reading /proc/self/fd is more reliable than calling lsof, so try that - // first. - numOpenFDs := func() (int, []byte, error) { - fds, err := os.ReadDir("/proc/self/fd") - if err != nil { - return 0, nil, err - } - return len(fds), nil, nil + if runtime.GOOS == "windows" { + t.Skip("we don't currently suppore counting open handles on windows") } - want, before, err := numOpenFDs() - if err != nil { - // We encountered a problem reading /proc/self/fd (we might be on - // a platform that doesn't have it). Fall back onto lsof. - t.Logf("using lsof because: %v", err) - numOpenFDs = func() (int, []byte, error) { - // Android's stock lsof does not obey the -p option, - // so extra filtering is needed. - // https://golang.org/issue/10206 - if runtime.GOOS == "android" { - // numOpenFDsAndroid handles errors itself and - // might skip or fail the test. - n, lsof := numOpenFDsAndroid(t) - return n, lsof, nil - } - lsof, err := exec.Command("lsof", "-b", "-n", "-p", strconv.Itoa(os.Getpid())).Output() - return bytes.Count(lsof, []byte("\n")), lsof, err - } - - // lsof may see file descriptors associated with the fork itself, - // so we allow some extra margin if we have to use it. - // https://golang.org/issue/19243 - tolerance = 5 - // Retry reading the number of open file descriptors. - want, before, err = numOpenFDs() - if err != nil { - t.Log(err) - t.Skipf("skipping test; error finding or running lsof") + openFDs := func() []uintptr { + var fds []uintptr + for i := uintptr(0); i < 100; i++ { + if fdtest.Exists(i) { + fds = append(fds, i) + } } + return fds } + want := openFDs() for i := 0; i < 6; i++ { cmd := exec.Command("something-that-does-not-exist-executable") cmd.StdoutPipe() @@ -432,59 +387,10 @@ func TestPipeLookPathLeak(t *testing.T) { t.Fatal("unexpected success") } } - got, after, err := numOpenFDs() - if err != nil { - // numOpenFDs has already succeeded once, it should work here. - t.Errorf("unexpected failure: %v", err) - } - if got-want > tolerance { - t.Errorf("number of open file descriptors changed: got %v, want %v", got, want) - if before != nil { - t.Errorf("before:\n%v\n", before) - } - if after != nil { - t.Errorf("after:\n%v\n", after) - } - } -} - -func numOpenFDsAndroid(t *testing.T) (n int, lsof []byte) { - raw, err := exec.Command("lsof").Output() - if err != nil { - t.Skip("skipping test; error finding or running lsof") - } - - // First find the PID column index by parsing the first line, and - // select lines containing pid in the column. - pid := []byte(strconv.Itoa(os.Getpid())) - pidCol := -1 - - s := bufio.NewScanner(bytes.NewReader(raw)) - for s.Scan() { - line := s.Bytes() - fields := bytes.Fields(line) - if pidCol < 0 { - for i, v := range fields { - if bytes.Equal(v, []byte("PID")) { - pidCol = i - break - } - } - lsof = append(lsof, line...) - continue - } - if bytes.Equal(fields[pidCol], pid) { - lsof = append(lsof, '\n') - lsof = append(lsof, line...) - } - } - if pidCol < 0 { - t.Fatal("error processing lsof output: unexpected header format") - } - if err := s.Err(); err != nil { - t.Fatalf("error processing lsof output: %v", err) + got := openFDs() + if !reflect.DeepEqual(got, want) { + t.Errorf("set of open file descriptors changed: got %v, want %v", got, want) } - return bytes.Count(lsof, []byte("\n")), lsof } func TestExtraFilesFDShuffle(t *testing.T) { @@ -794,7 +700,7 @@ func TestHelperProcess(*testing.T) { cmd, args := args[0], args[1:] switch cmd { case "echo": - iargs := []interface{}{} + iargs := []any{} for _, s := range args { iargs = append(iargs, s) } @@ -1048,6 +954,14 @@ func TestContext(t *testing.T) { } func TestContextCancel(t *testing.T) { + if runtime.GOOS == "netbsd" && runtime.GOARCH == "arm64" { + testenv.SkipFlaky(t, 42061) + } + + // To reduce noise in the final goroutine dump, + // let other parallel tests complete if possible. + t.Parallel() + ctx, cancel := context.WithCancel(context.Background()) defer cancel() c := helperCommandContext(t, ctx, "cat") @@ -1072,14 +986,25 @@ func TestContextCancel(t *testing.T) { // Calling cancel should have killed the process, so writes // should now fail. Give the process a little while to die. start := time.Now() + delay := 1 * time.Millisecond for { if _, err := io.WriteString(stdin, "echo"); err != nil { break } + if time.Since(start) > time.Minute { - t.Fatal("canceling context did not stop program") + // Panic instead of calling t.Fatal so that we get a goroutine dump. + // We want to know exactly what the os/exec goroutines got stuck on. + panic("canceling context did not stop program") + } + + // Back off exponentially (up to 1-second sleeps) to give the OS time to + // terminate the process. + delay *= 2 + if delay > 1*time.Second { + delay = 1 * time.Second } - time.Sleep(time.Millisecond) + time.Sleep(delay) } if err := c.Wait(); err == nil { diff --git a/libgo/go/os/exec/exec_unix.go b/libgo/go/os/exec/exec_unix.go index 467c069e1ca..c20f35276c6 100644 --- a/libgo/go/os/exec/exec_unix.go +++ b/libgo/go/os/exec/exec_unix.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !plan9 && !windows -// +build !plan9,!windows package exec diff --git a/libgo/go/os/exec/exec_windows_test.go b/libgo/go/os/exec/exec_windows_test.go index fbccffec0e9..8e31e47190f 100644 --- a/libgo/go/os/exec/exec_windows_test.go +++ b/libgo/go/os/exec/exec_windows_test.go @@ -3,13 +3,13 @@ // license that can be found in the LICENSE file. //go:build windows -// +build windows package exec_test import ( "io" "os" + "os/exec" "strconv" "syscall" "testing" @@ -41,3 +41,16 @@ func TestPipePassing(t *testing.T) { t.Error(err) } } + +func TestNoInheritHandles(t *testing.T) { + cmd := exec.Command("cmd", "/c exit 88") + cmd.SysProcAttr = &syscall.SysProcAttr{NoInheritHandles: true} + err := cmd.Run() + exitError, ok := err.(*exec.ExitError) + if !ok { + t.Fatalf("got error %v; want ExitError", err) + } + if exitError.ExitCode() != 88 { + t.Fatalf("got exit code %d; want 88", exitError.ExitCode()) + } +} diff --git a/libgo/go/os/exec/internal/fdtest/exists_js.go b/libgo/go/os/exec/internal/fdtest/exists_js.go new file mode 100644 index 00000000000..a7ce33c74f4 --- /dev/null +++ b/libgo/go/os/exec/internal/fdtest/exists_js.go @@ -0,0 +1,18 @@ +// Copyright 2021 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. + +//go:build js + +package fdtest + +import ( + "syscall" +) + +// Exists returns true if fd is a valid file descriptor. +func Exists(fd uintptr) bool { + var s syscall.Stat_t + err := syscall.Fstat(int(fd), &s) + return err != syscall.EBADF +} diff --git a/libgo/go/os/exec/internal/fdtest/exists_plan9.go b/libgo/go/os/exec/internal/fdtest/exists_plan9.go new file mode 100644 index 00000000000..8886e060271 --- /dev/null +++ b/libgo/go/os/exec/internal/fdtest/exists_plan9.go @@ -0,0 +1,20 @@ +// Copyright 2021 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. + +//go:build plan9 + +package fdtest + +import ( + "syscall" +) + +const errBadFd = syscall.ErrorString("fd out of range or not open") + +// Exists returns true if fd is a valid file descriptor. +func Exists(fd uintptr) bool { + var buf [1]byte + _, err := syscall.Fstat(int(fd), buf[:]) + return err != errBadFd +} diff --git a/libgo/go/os/exec/internal/fdtest/exists_test.go b/libgo/go/os/exec/internal/fdtest/exists_test.go new file mode 100644 index 00000000000..a02dddf7f7d --- /dev/null +++ b/libgo/go/os/exec/internal/fdtest/exists_test.go @@ -0,0 +1,21 @@ +// Copyright 2021 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 fdtest + +import ( + "os" + "runtime" + "testing" +) + +func TestExists(t *testing.T) { + if runtime.GOOS == "windows" { + t.Skip("Exists not implemented for windows") + } + + if !Exists(os.Stdout.Fd()) { + t.Errorf("Exists(%d) got false want true", os.Stdout.Fd()) + } +} diff --git a/libgo/go/os/exec/internal/fdtest/exists_unix.go b/libgo/go/os/exec/internal/fdtest/exists_unix.go new file mode 100644 index 00000000000..49f264cebda --- /dev/null +++ b/libgo/go/os/exec/internal/fdtest/exists_unix.go @@ -0,0 +1,19 @@ +// Copyright 2021 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. + +//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris + +// Package fdtest provides test helpers for working with file descriptors across exec. +package fdtest + +import ( + "syscall" +) + +// Exists returns true if fd is a valid file descriptor. +func Exists(fd uintptr) bool { + var s syscall.Stat_t + err := syscall.Fstat(int(fd), &s) + return err != syscall.EBADF +} diff --git a/libgo/go/os/exec/internal/fdtest/exists_windows.go b/libgo/go/os/exec/internal/fdtest/exists_windows.go new file mode 100644 index 00000000000..72b8ccfd23c --- /dev/null +++ b/libgo/go/os/exec/internal/fdtest/exists_windows.go @@ -0,0 +1,12 @@ +// Copyright 2021 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. + +//go:build windows + +package fdtest + +// Exists is not implemented on windows and panics. +func Exists(fd uintptr) bool { + panic("unimplemented") +} diff --git a/libgo/go/os/exec/lp_js.go b/libgo/go/os/exec/lp_js.go index 4eac25fe6f4..54ddc4d5b48 100644 --- a/libgo/go/os/exec/lp_js.go +++ b/libgo/go/os/exec/lp_js.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build js && wasm -// +build js,wasm package exec diff --git a/libgo/go/os/exec/lp_unix.go b/libgo/go/os/exec/lp_unix.go index ebecc74d2a1..0935ab9ae4c 100644 --- a/libgo/go/os/exec/lp_unix.go +++ b/libgo/go/os/exec/lp_unix.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || hurd || linux || netbsd || openbsd || solaris -// +build aix darwin dragonfly freebsd hurd linux netbsd openbsd solaris package exec diff --git a/libgo/go/os/exec/lp_unix_test.go b/libgo/go/os/exec/lp_unix_test.go index fe4df1a0610..bf602fcedb7 100644 --- a/libgo/go/os/exec/lp_unix_test.go +++ b/libgo/go/os/exec/lp_unix_test.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || hurd || linux || netbsd || openbsd || solaris -// +build aix darwin dragonfly freebsd hurd linux netbsd openbsd solaris package exec diff --git a/libgo/go/os/exec/read3.go b/libgo/go/os/exec/read3.go index a8c71831d88..10cbfbd54ab 100644 --- a/libgo/go/os/exec/read3.go +++ b/libgo/go/os/exec/read3.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build ignore -// +build ignore // This is a test program that verifies that it can read from // descriptor 3 and that no other descriptors are open. @@ -19,12 +18,15 @@ import ( "io" "os" "os/exec" + "os/exec/internal/fdtest" "runtime" "strings" ) func main() { fd3 := os.NewFile(3, "fd3") + defer fd3.Close() + bs, err := io.ReadAll(fd3) if err != nil { fmt.Printf("ReadAll from fd 3: %v\n", err) @@ -38,65 +40,52 @@ func main() { // descriptor from parent == 3 // All descriptors 4 and up should be available, // except for any used by the network poller. - var files []*os.File - for wantfd := uintptr(4); wantfd <= 100; wantfd++ { - if poll.IsPollDescriptor(wantfd) { + for fd := uintptr(4); fd <= 100; fd++ { + if poll.IsPollDescriptor(fd) { continue } - f, err := os.Open(os.Args[0]) - if err != nil { - fmt.Printf("error opening file with expected fd %d: %v", wantfd, err) - os.Exit(1) + + if !fdtest.Exists(fd) { + continue } - if got := f.Fd(); got != wantfd { - fmt.Printf("leaked parent file. fd = %d; want %d\n", got, wantfd) - fdfile := fmt.Sprintf("/proc/self/fd/%d", wantfd) - link, err := os.Readlink(fdfile) - fmt.Printf("readlink(%q) = %q, %v\n", fdfile, link, err) - var args []string - switch runtime.GOOS { - case "plan9": - args = []string{fmt.Sprintf("/proc/%d/fd", os.Getpid())} - case "aix", "solaris", "illumos": - args = []string{fmt.Sprint(os.Getpid())} - default: - args = []string{"-p", fmt.Sprint(os.Getpid())} - } - // Determine which command to use to display open files. - ofcmd := "lsof" - switch runtime.GOOS { - case "dragonfly", "freebsd", "netbsd", "openbsd": - ofcmd = "fstat" - case "plan9": - ofcmd = "/bin/cat" - case "aix": - ofcmd = "procfiles" - case "solaris", "illumos": - ofcmd = "pfiles" - } + fmt.Printf("leaked parent file. fdtest.Exists(%d) got true want false\n", fd) + + fdfile := fmt.Sprintf("/proc/self/fd/%d", fd) + link, err := os.Readlink(fdfile) + fmt.Printf("readlink(%q) = %q, %v\n", fdfile, link, err) - cmd := exec.Command(ofcmd, args...) - out, err := cmd.CombinedOutput() - if err != nil { - fmt.Fprintf(os.Stderr, "%s failed: %v\n", strings.Join(cmd.Args, " "), err) - } - fmt.Printf("%s", out) - os.Exit(1) + var args []string + switch runtime.GOOS { + case "plan9": + args = []string{fmt.Sprintf("/proc/%d/fd", os.Getpid())} + case "aix", "solaris", "illumos": + args = []string{fmt.Sprint(os.Getpid())} + default: + args = []string{"-p", fmt.Sprint(os.Getpid())} } - files = append(files, f) - } - for _, f := range files { - f.Close() - } + // Determine which command to use to display open files. + ofcmd := "lsof" + switch runtime.GOOS { + case "dragonfly", "freebsd", "netbsd", "openbsd": + ofcmd = "fstat" + case "plan9": + ofcmd = "/bin/cat" + case "aix": + ofcmd = "procfiles" + case "solaris", "illumos": + ofcmd = "pfiles" + } - // Referring to fd3 here ensures that it is not - // garbage collected, and therefore closed, while - // executing the wantfd loop above. It doesn't matter - // what we do with fd3 as long as we refer to it; - // closing it is the easy choice. - fd3.Close() + cmd := exec.Command(ofcmd, args...) + out, err := cmd.CombinedOutput() + if err != nil { + fmt.Fprintf(os.Stderr, "%s failed: %v\n", strings.Join(cmd.Args, " "), err) + } + fmt.Printf("%s", out) + os.Exit(1) + } os.Stdout.Write(bs) } diff --git a/libgo/go/os/exec_plan9.go b/libgo/go/os/exec_plan9.go index cc84f976696..69714ff7983 100644 --- a/libgo/go/os/exec_plan9.go +++ b/libgo/go/os/exec_plan9.go @@ -115,11 +115,11 @@ func (p *ProcessState) success() bool { return p.status.ExitStatus() == 0 } -func (p *ProcessState) sys() interface{} { +func (p *ProcessState) sys() any { return p.status } -func (p *ProcessState) sysUsage() interface{} { +func (p *ProcessState) sysUsage() any { return p.status } diff --git a/libgo/go/os/exec_posix.go b/libgo/go/os/exec_posix.go index eb5cbc8a953..9514f07acf0 100644 --- a/libgo/go/os/exec_posix.go +++ b/libgo/go/os/exec_posix.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || hurd || (js && wasm) || linux || netbsd || openbsd || solaris || windows -// +build aix darwin dragonfly freebsd hurd js,wasm linux netbsd openbsd solaris windows package os @@ -88,11 +87,11 @@ func (p *ProcessState) success() bool { return p.status.ExitStatus() == 0 } -func (p *ProcessState) sys() interface{} { +func (p *ProcessState) sys() any { return p.status } -func (p *ProcessState) sysUsage() interface{} { +func (p *ProcessState) sysUsage() any { return p.rusage } diff --git a/libgo/go/os/exec_unix.go b/libgo/go/os/exec_unix.go index 4ada050f5b6..e777de44ee2 100644 --- a/libgo/go/os/exec_unix.go +++ b/libgo/go/os/exec_unix.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || hurd || (js && wasm) || linux || netbsd || openbsd || solaris -// +build aix darwin dragonfly freebsd hurd js,wasm linux netbsd openbsd solaris package os diff --git a/libgo/go/os/exec_unix_test.go b/libgo/go/os/exec_unix_test.go index f14b3519fbe..fa332bf1ed8 100644 --- a/libgo/go/os/exec_unix_test.go +++ b/libgo/go/os/exec_unix_test.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris package os_test diff --git a/libgo/go/os/executable_path.go b/libgo/go/os/executable_path.go index 625430ecfca..d6161bcb08b 100644 --- a/libgo/go/os/executable_path.go +++ b/libgo/go/os/executable_path.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || openbsd -// +build aix openbsd package os diff --git a/libgo/go/os/executable_plan9.go b/libgo/go/os/executable_plan9.go index ad7a4410dcd..8d8c83260f5 100644 --- a/libgo/go/os/executable_plan9.go +++ b/libgo/go/os/executable_plan9.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build plan9 -// +build plan9 package os diff --git a/libgo/go/os/executable_procfs.go b/libgo/go/os/executable_procfs.go index fdb239474c4..eaa9d92a37e 100644 --- a/libgo/go/os/executable_procfs.go +++ b/libgo/go/os/executable_procfs.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build hurd || linux || netbsd || (js && wasm) -// +build hurd linux netbsd js,wasm package os diff --git a/libgo/go/os/executable_sysctl.go b/libgo/go/os/executable_sysctl.go index 039448b5573..3c2aeacf7da 100644 --- a/libgo/go/os/executable_sysctl.go +++ b/libgo/go/os/executable_sysctl.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build freebsd || dragonfly -// +build freebsd dragonfly package os diff --git a/libgo/go/os/export_unix_test.go b/libgo/go/os/export_unix_test.go index dd2f8ec94a5..3a72db8542e 100644 --- a/libgo/go/os/export_unix_test.go +++ b/libgo/go/os/export_unix_test.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || hurd || (js && wasm) || linux || netbsd || openbsd || solaris -// +build aix darwin dragonfly freebsd hurd js,wasm linux netbsd openbsd solaris package os diff --git a/libgo/go/os/fifo_test.go b/libgo/go/os/fifo_test.go index 007ed291293..de709279613 100644 --- a/libgo/go/os/fifo_test.go +++ b/libgo/go/os/fifo_test.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd -// +build darwin dragonfly freebsd linux netbsd openbsd package os_test diff --git a/libgo/go/os/file.go b/libgo/go/os/file.go index e717f171e72..28231285542 100644 --- a/libgo/go/os/file.go +++ b/libgo/go/os/file.go @@ -109,7 +109,7 @@ func (e *LinkError) Unwrap() error { return e.Err } -// Read reads up to len(b) bytes from the File. +// Read reads up to len(b) bytes from the File and stores them in b. // It returns the number of bytes read and any error encountered. // At end of file, Read returns 0, io.EOF. func (f *File) Read(b []byte) (n int, err error) { @@ -166,7 +166,7 @@ type onlyWriter struct { io.Writer } -// Write writes len(b) bytes to the File. +// Write writes len(b) bytes from b to the File. // It returns the number of bytes written and an error, if any. // Write returns a non-nil error when n != len(b). func (f *File) Write(b []byte) (n int, err error) { diff --git a/libgo/go/os/file_plan9.go b/libgo/go/os/file_plan9.go index 604aea607bc..887e1c88923 100644 --- a/libgo/go/os/file_plan9.go +++ b/libgo/go/os/file_plan9.go @@ -139,7 +139,7 @@ func openFileNolog(name string, flag int, perm FileMode) (*File, error) { // Close closes the File, rendering it unusable for I/O. // On files that support SetDeadline, any pending I/O operations will -// be canceled and return immediately with an error. +// be canceled and return immediately with an ErrClosed error. // Close will return an error if it has already been called. func (f *File) Close() error { if err := f.checkValid("close"); err != nil { diff --git a/libgo/go/os/file_posix.go b/libgo/go/os/file_posix.go index 21831a7031b..8612bb37966 100644 --- a/libgo/go/os/file_posix.go +++ b/libgo/go/os/file_posix.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || hurd || (js && wasm) || linux || netbsd || openbsd || solaris || windows -// +build aix darwin dragonfly freebsd hurd js,wasm linux netbsd openbsd solaris windows package os @@ -17,7 +16,7 @@ func sigpipe() // implemented in package runtime // Close closes the File, rendering it unusable for I/O. // On files that support SetDeadline, any pending I/O operations will -// be canceled and return immediately with an error. +// be canceled and return immediately with an ErrClosed error. // Close will return an error if it has already been called. func (f *File) Close() error { if f == nil { diff --git a/libgo/go/os/file_unix.go b/libgo/go/os/file_unix.go index a31800bd6aa..188ccda29ce 100644 --- a/libgo/go/os/file_unix.go +++ b/libgo/go/os/file_unix.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || hurd || (js && wasm) || linux || netbsd || openbsd || solaris -// +build aix darwin dragonfly freebsd hurd js,wasm linux netbsd openbsd solaris package os diff --git a/libgo/go/os/os_test.go b/libgo/go/os/os_test.go index 0cde45423b8..e90b89dfa1b 100644 --- a/libgo/go/os/os_test.go +++ b/libgo/go/os/os_test.go @@ -52,34 +52,31 @@ var sysdir = func() *sysDir { "libpowermanager.so", }, } - case "darwin", "ios": - switch runtime.GOARCH { - case "arm64": - wd, err := syscall.Getwd() - if err != nil { - wd = err.Error() - } - sd := &sysDir{ - filepath.Join(wd, "..", ".."), - []string{ - "ResourceRules.plist", - "Info.plist", - }, - } - found := true - for _, f := range sd.files { - path := filepath.Join(sd.name, f) - if _, err := Stat(path); err != nil { - found = false - break - } - } - if found { - return sd + case "ios": + wd, err := syscall.Getwd() + if err != nil { + wd = err.Error() + } + sd := &sysDir{ + filepath.Join(wd, "..", ".."), + []string{ + "ResourceRules.plist", + "Info.plist", + }, + } + found := true + for _, f := range sd.files { + path := filepath.Join(sd.name, f) + if _, err := Stat(path); err != nil { + found = false + break } - // In a self-hosted iOS build the above files might - // not exist. Look for system files instead below. } + if found { + return sd + } + // In a self-hosted iOS build the above files might + // not exist. Look for system files instead below. case "windows": return &sysDir{ Getenv("SystemRoot") + "\\system32\\drivers\\etc", @@ -113,20 +110,16 @@ func size(name string, t *testing.T) int64 { if err != nil { t.Fatal("open failed:", err) } - defer file.Close() - var buf [100]byte - len := 0 - for { - n, e := file.Read(buf[0:]) - len += n - if e == io.EOF { - break - } - if e != nil { - t.Fatal("read failed:", e) + defer func() { + if err := file.Close(); err != nil { + t.Error(err) } + }() + n, err := io.Copy(io.Discard, file) + if err != nil { + t.Fatal(err) } - return int64(len) + return n } func equal(name1, name2 string) (r bool) { @@ -142,13 +135,8 @@ func equal(name1, name2 string) (r bool) { // localTmp returns a local temporary directory not on NFS. func localTmp() string { switch runtime.GOOS { - case "android", "windows": + case "android", "ios", "windows": return TempDir() - case "darwin", "ios": - switch runtime.GOARCH { - case "arm64": - return TempDir() - } } return "/tmp" } @@ -572,15 +560,12 @@ func TestReaddirnamesOneAtATime(t *testing.T) { switch runtime.GOOS { case "android": dir = "/system/bin" - case "darwin", "ios": - switch runtime.GOARCH { - case "arm64": - wd, err := Getwd() - if err != nil { - t.Fatal(err) - } - dir = wd + case "ios": + wd, err := Getwd() + if err != nil { + t.Fatal(err) } + dir = wd case "plan9": dir = "/bin" case "windows": @@ -1401,22 +1386,19 @@ func TestChdirAndGetwd(t *testing.T) { dirs = []string{"/system/bin"} case "plan9": dirs = []string{"/", "/usr"} - case "darwin", "ios": - switch runtime.GOARCH { - case "arm64": - dirs = nil - for _, d := range []string{"d1", "d2"} { - dir, err := os.MkdirTemp("", d) - if err != nil { - t.Fatalf("TempDir: %v", err) - } - // Expand symlinks so path equality tests work. - dir, err = filepath.EvalSymlinks(dir) - if err != nil { - t.Fatalf("EvalSymlinks: %v", err) - } - dirs = append(dirs, dir) + case "ios": + dirs = nil + for _, d := range []string{"d1", "d2"} { + dir, err := os.MkdirTemp("", d) + if err != nil { + t.Fatalf("TempDir: %v", err) + } + // Expand symlinks so path equality tests work. + dir, err = filepath.EvalSymlinks(dir) + if err != nil { + t.Fatalf("EvalSymlinks: %v", err) } + dirs = append(dirs, dir) } } oldwd := Getenv("PWD") @@ -1763,8 +1745,8 @@ func TestHostname(t *testing.T) { // and the /bin/hostname only returns the first component want := runBinHostname(t) if hostname != want { - i := strings.Index(hostname, ".") - if i < 0 || hostname[0:i] != want { + host, _, ok := strings.Cut(hostname, ".") + if !ok || host != want { t.Errorf("Hostname() = %q, want %q", hostname, want) } } @@ -2440,8 +2422,6 @@ func TestRemoveAllRace(t *testing.T) { // Test that reading from a pipe doesn't use up a thread. func TestPipeThreads(t *testing.T) { switch runtime.GOOS { - case "freebsd": - t.Skip("skipping on FreeBSD; issue 19093") case "illumos", "solaris": t.Skip("skipping on Solaris and illumos; issue 19111") case "windows": diff --git a/libgo/go/os/os_unix_test.go b/libgo/go/os/os_unix_test.go index 95ceb0daa36..10056daa7e1 100644 --- a/libgo/go/os/os_unix_test.go +++ b/libgo/go/os/os_unix_test.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || hurd || (js && wasm) || linux || netbsd || openbsd || solaris -// +build aix darwin dragonfly freebsd hurd js,wasm linux netbsd openbsd solaris package os_test diff --git a/libgo/go/os/path_test.go b/libgo/go/os/path_test.go index 4535abbc527..59f72834859 100644 --- a/libgo/go/os/path_test.go +++ b/libgo/go/os/path_test.go @@ -96,13 +96,8 @@ func TestMkdirAllWithSymlink(t *testing.T) { func TestMkdirAllAtSlash(t *testing.T) { switch runtime.GOOS { - case "android", "plan9", "windows": + case "android", "ios", "plan9", "windows": t.Skipf("skipping on %s", runtime.GOOS) - case "darwin", "ios": - switch runtime.GOARCH { - case "arm64": - t.Skipf("skipping on darwin/arm64, mkdir returns EPERM") - } } RemoveAll("/_go_os_test") const dir = "/_go_os_test/dir" diff --git a/libgo/go/os/path_unix.go b/libgo/go/os/path_unix.go index bb0b0a05da7..26574f6a2e1 100644 --- a/libgo/go/os/path_unix.go +++ b/libgo/go/os/path_unix.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || hurd || (js && wasm) || linux || netbsd || openbsd || solaris -// +build aix darwin dragonfly freebsd hurd js,wasm linux netbsd openbsd solaris package os diff --git a/libgo/go/os/pipe2_bsd.go b/libgo/go/os/pipe2_bsd.go index bf6d081db5b..7eb1350d025 100644 --- a/libgo/go/os/pipe2_bsd.go +++ b/libgo/go/os/pipe2_bsd.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build dragonfly || freebsd || netbsd || openbsd -// +build dragonfly freebsd netbsd openbsd package os diff --git a/libgo/go/os/pipe2_illumos.go b/libgo/go/os/pipe2_illumos.go index 71b8cb8e25b..354b35cc462 100644 --- a/libgo/go/os/pipe2_illumos.go +++ b/libgo/go/os/pipe2_illumos.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build illumos -// +build illumos package os diff --git a/libgo/go/os/pipe_bsd.go b/libgo/go/os/pipe_bsd.go index 097b32e7eb3..554d62111a5 100644 --- a/libgo/go/os/pipe_bsd.go +++ b/libgo/go/os/pipe_bsd.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || (js && wasm) || (solaris && !illumos) -// +build aix darwin js,wasm solaris,!illumos package os diff --git a/libgo/go/os/pipe_test.go b/libgo/go/os/pipe_test.go index 41a1e9c78aa..20716bce1e1 100644 --- a/libgo/go/os/pipe_test.go +++ b/libgo/go/os/pipe_test.go @@ -4,7 +4,6 @@ // Test broken pipes on Unix systems. //go:build !plan9 && !js -// +build !plan9,!js package os_test @@ -151,11 +150,6 @@ func TestStdPipeHelper(t *testing.T) { } func testClosedPipeRace(t *testing.T, read bool) { - switch runtime.GOOS { - case "freebsd": - t.Skip("FreeBSD does not use the poller; issue 19093") - } - limit := 1 if !read { // Get the amount we have to write to overload a pipe diff --git a/libgo/go/os/rawconn.go b/libgo/go/os/rawconn.go index ffc598b0618..14a495d9c0a 100644 --- a/libgo/go/os/rawconn.go +++ b/libgo/go/os/rawconn.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !plan9 -// +build !plan9 package os diff --git a/libgo/go/os/rawconn_test.go b/libgo/go/os/rawconn_test.go index 8aebaf87a60..fd2038a2332 100644 --- a/libgo/go/os/rawconn_test.go +++ b/libgo/go/os/rawconn_test.go @@ -4,7 +4,6 @@ // Test use of raw connections. //go:build !plan9 && !js -// +build !plan9,!js package os_test diff --git a/libgo/go/os/readfrom_stub.go b/libgo/go/os/readfrom_stub.go index 826760f3df6..8b7d5fb8f9e 100644 --- a/libgo/go/os/readfrom_stub.go +++ b/libgo/go/os/readfrom_stub.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !linux -// +build !linux package os diff --git a/libgo/go/os/removeall_at.go b/libgo/go/os/removeall_at.go index 4e55577dc2a..bc5376f1cbc 100644 --- a/libgo/go/os/removeall_at.go +++ b/libgo/go/os/removeall_at.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || hurd || linux || netbsd || openbsd || solaris -// +build aix darwin dragonfly freebsd hurd linux netbsd openbsd solaris package os diff --git a/libgo/go/os/removeall_noat.go b/libgo/go/os/removeall_noat.go index 966f3f59bc1..a85b1f95669 100644 --- a/libgo/go/os/removeall_noat.go +++ b/libgo/go/os/removeall_noat.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !aix && !darwin && !dragonfly && !freebsd && !hurd && !linux && !netbsd && !openbsd && !solaris -// +build !aix,!darwin,!dragonfly,!freebsd,!hurd,!linux,!netbsd,!openbsd,!solaris package os diff --git a/libgo/go/os/signal/example_unix_test.go b/libgo/go/os/signal/example_unix_test.go index 3f7795b8cf9..b279ee9491f 100644 --- a/libgo/go/os/signal/example_unix_test.go +++ b/libgo/go/os/signal/example_unix_test.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris package signal_test diff --git a/libgo/go/os/signal/internal/pty/pty.go b/libgo/go/os/signal/internal/pty/pty.go index 4ed3eb44e17..e5ee3f6dc01 100644 --- a/libgo/go/os/signal/internal/pty/pty.go +++ b/libgo/go/os/signal/internal/pty/pty.go @@ -2,9 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build (aix || darwin || dragonfly || freebsd || hurd || (linux && !android) || netbsd || openbsd || solaris) && cgo -// +build aix darwin dragonfly freebsd hurd linux,!android netbsd openbsd solaris -// +build cgo +//go:build (aix || darwin || dragonfly || freebsd || hurd || (linux && !android) || netbsd || openbsd) && cgo // Package pty is a simple pseudo-terminal package for Unix systems, // implemented by calling C functions via cgo. diff --git a/libgo/go/os/signal/signal_cgo_test.go b/libgo/go/os/signal/signal_cgo_test.go index e1e4509e2a7..67bad66e0b2 100644 --- a/libgo/go/os/signal/signal_cgo_test.go +++ b/libgo/go/os/signal/signal_cgo_test.go @@ -3,8 +3,6 @@ // license that can be found in the LICENSE file. //go:build (darwin || dragonfly || freebsd || (linux && !android) || netbsd || openbsd) && cgo -// +build darwin dragonfly freebsd linux,!android netbsd openbsd -// +build cgo // Note that this test does not work on Solaris: issue #22849. // Don't run the test on Android because at least some versions of the diff --git a/libgo/go/os/signal/signal_linux_test.go b/libgo/go/os/signal/signal_linux_test.go index 7abe1ec5a0a..f70f108442b 100644 --- a/libgo/go/os/signal/signal_linux_test.go +++ b/libgo/go/os/signal/signal_linux_test.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build linux -// +build linux package signal diff --git a/libgo/go/os/signal/signal_test.go b/libgo/go/os/signal/signal_test.go index c653c344fa1..7c8162ec7d7 100644 --- a/libgo/go/os/signal/signal_test.go +++ b/libgo/go/os/signal/signal_test.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || hurd || linux || netbsd || openbsd || solaris -// +build aix darwin dragonfly freebsd hurd linux netbsd openbsd solaris package signal @@ -137,6 +136,9 @@ func TestSignal(t *testing.T) { // Using 10 is arbitrary. c1 := make(chan os.Signal, 10) Notify(c1) + // Stop relaying the SIGURG signals. See #49724 + Reset(syscall.SIGURG) + defer Stop(c1) // Send this process a SIGWINCH t.Logf("sigwinch...") diff --git a/libgo/go/os/signal/signal_unix.go b/libgo/go/os/signal/signal_unix.go index a0c7ffbdb54..45675780555 100644 --- a/libgo/go/os/signal/signal_unix.go +++ b/libgo/go/os/signal/signal_unix.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || hurd || (js && wasm) || linux || netbsd || openbsd || solaris || windows -// +build aix darwin dragonfly freebsd hurd js,wasm linux netbsd openbsd solaris windows package signal diff --git a/libgo/go/os/stat_js.go b/libgo/go/os/stat_js.go index 3badf5ba575..c3e9b5b5e53 100644 --- a/libgo/go/os/stat_js.go +++ b/libgo/go/os/stat_js.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build js && wasm -// +build js,wasm package os diff --git a/libgo/go/os/stat_plan9.go b/libgo/go/os/stat_plan9.go index 57ae6fb0bbd..e20accf1913 100644 --- a/libgo/go/os/stat_plan9.go +++ b/libgo/go/os/stat_plan9.go @@ -43,7 +43,7 @@ func fileInfoFromStat(d *syscall.Dir) *fileStat { } // arg is an open *File or a path string. -func dirstat(arg interface{}) (*syscall.Dir, error) { +func dirstat(arg any) (*syscall.Dir, error) { var name string var err error diff --git a/libgo/go/os/stat_unix.go b/libgo/go/os/stat_unix.go index 6ae26ad540f..b4ab62019dd 100644 --- a/libgo/go/os/stat_unix.go +++ b/libgo/go/os/stat_unix.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || hurd || (js && wasm) || linux || netbsd || openbsd || solaris -// +build aix darwin dragonfly freebsd hurd js,wasm linux netbsd openbsd solaris package os diff --git a/libgo/go/os/sticky_bsd.go b/libgo/go/os/sticky_bsd.go index ab23d8111d3..e71daf7c749 100644 --- a/libgo/go/os/sticky_bsd.go +++ b/libgo/go/os/sticky_bsd.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || (js && wasm) || netbsd || openbsd || solaris -// +build aix darwin dragonfly freebsd js,wasm netbsd openbsd solaris package os diff --git a/libgo/go/os/sticky_notbsd.go b/libgo/go/os/sticky_notbsd.go index 9979b43e8e6..9a87fbde92c 100644 --- a/libgo/go/os/sticky_notbsd.go +++ b/libgo/go/os/sticky_notbsd.go @@ -3,14 +3,6 @@ // license that can be found in the LICENSE file. //go:build !aix && !darwin && !dragonfly && !freebsd && (!js || !wasm) && !netbsd && !openbsd && !solaris -// +build !aix -// +build !darwin -// +build !dragonfly -// +build !freebsd -// +build !js !wasm -// +build !netbsd -// +build !openbsd -// +build !solaris package os diff --git a/libgo/go/os/sys_bsd.go b/libgo/go/os/sys_bsd.go index 1e245eb53a2..e272c245717 100644 --- a/libgo/go/os/sys_bsd.go +++ b/libgo/go/os/sys_bsd.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build darwin || dragonfly || freebsd || (js && wasm) || netbsd || openbsd -// +build darwin dragonfly freebsd js,wasm netbsd openbsd package os diff --git a/libgo/go/os/sys_js.go b/libgo/go/os/sys_js.go index 4d6a64e8ebe..4fd0e2d7c7d 100644 --- a/libgo/go/os/sys_js.go +++ b/libgo/go/os/sys_js.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build js && wasm -// +build js,wasm package os diff --git a/libgo/go/os/sys_unix.go b/libgo/go/os/sys_unix.go index aab683e4db8..273fc0351e0 100644 --- a/libgo/go/os/sys_unix.go +++ b/libgo/go/os/sys_unix.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || hurd || linux || netbsd || openbsd || solaris -// +build aix darwin dragonfly freebsd hurd linux netbsd openbsd solaris package os diff --git a/libgo/go/os/timeout_test.go b/libgo/go/os/timeout_test.go index 6d65e420f04..2ff58110d65 100644 --- a/libgo/go/os/timeout_test.go +++ b/libgo/go/os/timeout_test.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !js && !plan9 && !windows -// +build !js,!plan9,!windows package os_test diff --git a/libgo/go/os/types_plan9.go b/libgo/go/os/types_plan9.go index 125da661b79..ccf4fd932e7 100644 --- a/libgo/go/os/types_plan9.go +++ b/libgo/go/os/types_plan9.go @@ -15,13 +15,13 @@ type fileStat struct { size int64 mode FileMode modTime time.Time - sys interface{} + sys any } func (fs *fileStat) Size() int64 { return fs.size } func (fs *fileStat) Mode() FileMode { return fs.mode } func (fs *fileStat) ModTime() time.Time { return fs.modTime } -func (fs *fileStat) Sys() interface{} { return fs.sys } +func (fs *fileStat) Sys() any { return fs.sys } func sameFile(fs1, fs2 *fileStat) bool { a := fs1.sys.(*syscall.Dir) diff --git a/libgo/go/os/types_unix.go b/libgo/go/os/types_unix.go index e9b8b8ba3ac..1b90a5a1415 100644 --- a/libgo/go/os/types_unix.go +++ b/libgo/go/os/types_unix.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !windows && !plan9 -// +build !windows,!plan9 package os @@ -24,7 +23,7 @@ type fileStat struct { func (fs *fileStat) Size() int64 { return fs.size } func (fs *fileStat) Mode() FileMode { return fs.mode } func (fs *fileStat) ModTime() time.Time { return fs.modTime } -func (fs *fileStat) Sys() interface{} { return &fs.sys } +func (fs *fileStat) Sys() any { return &fs.sys } func sameFile(fs1, fs2 *fileStat) bool { return fs1.sys.Dev == fs2.sys.Dev && fs1.sys.Ino == fs2.sys.Ino diff --git a/libgo/go/os/types_windows.go b/libgo/go/os/types_windows.go index 59bf5ca3814..5443dfedc8c 100644 --- a/libgo/go/os/types_windows.go +++ b/libgo/go/os/types_windows.go @@ -138,7 +138,7 @@ func (fs *fileStat) ModTime() time.Time { } // Sys returns syscall.Win32FileAttributeData for file fs. -func (fs *fileStat) Sys() interface{} { +func (fs *fileStat) Sys() any { return &syscall.Win32FileAttributeData{ FileAttributes: fs.FileAttributes, CreationTime: fs.CreationTime, diff --git a/libgo/go/os/user/cgo_listgroups_unix.go b/libgo/go/os/user/cgo_listgroups_unix.go new file mode 100644 index 00000000000..5621c1a63aa --- /dev/null +++ b/libgo/go/os/user/cgo_listgroups_unix.go @@ -0,0 +1,43 @@ +// 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. + +//go:build (dragonfly || darwin || freebsd || (!android && linux) || netbsd || openbsd || (solaris && !illumos)) && cgo && !osusergo + +package user + +import ( + "fmt" + "strconv" + "syscall" + "unsafe" +) + +const maxGroups = 2048 + +func listGroups(u *User) ([]string, error) { + ug, err := strconv.Atoi(u.Gid) + if err != nil { + return nil, fmt.Errorf("user: list groups for %s: invalid gid %q", u.Username, u.Gid) + } + userGID := syscall.Gid_t(ug) + nameC := make([]byte, len(u.Username)+1) + copy(nameC, u.Username) + + n := int32(256) + gidsC := make([]syscall.Gid_t, n) + rv := getGroupList((*byte)(unsafe.Pointer(&nameC[0])), userGID, &gidsC[0], &n) + if rv == -1 { + // Mac is the only Unix that does not set n properly when rv == -1, so + // we need to use different logic for Mac vs. the other OS's. + if err := groupRetry(u.Username, nameC, userGID, &gidsC, &n); err != nil { + return nil, err + } + } + gidsC = gidsC[:n] + gids := make([]string, 0, n) + for _, g := range gidsC[:n] { + gids = append(gids, strconv.Itoa(int(g))) + } + return gids, nil +} diff --git a/libgo/go/os/user/cgo_lookup_unix.go b/libgo/go/os/user/cgo_lookup_unix.go index 3f4014c3633..17eee9ef4bd 100644 --- a/libgo/go/os/user/cgo_lookup_unix.go +++ b/libgo/go/os/user/cgo_lookup_unix.go @@ -3,9 +3,6 @@ // license that can be found in the LICENSE file. //go:build (aix || darwin || dragonfly || freebsd || hurd || (!android && linux) || netbsd || openbsd || solaris) && cgo && !osusergo -// +build aix darwin dragonfly freebsd hurd !android,linux netbsd openbsd solaris -// +build cgo -// +build !osusergo package user @@ -114,9 +111,7 @@ func buildUser(pwd *syscall.Passwd) *User { // say: "It is expected to be a comma separated list of // personal data where the first item is the full name of the // user." - if i := strings.Index(u.Name, ","); i >= 0 { - u.Name = u.Name[:i] - } + u.Name, _, _ = strings.Cut(u.Name, ",") return u } diff --git a/libgo/go/os/user/cgo_unix_test.go b/libgo/go/os/user/cgo_unix_test.go index 9ec32b3a788..6d16aa20b30 100644 --- a/libgo/go/os/user/cgo_unix_test.go +++ b/libgo/go/os/user/cgo_unix_test.go @@ -3,9 +3,6 @@ // license that can be found in the LICENSE file. //go:build (darwin || dragonfly || freebsd || (!android && linux) || netbsd || openbsd || solaris) && cgo && !osusergo -// +build darwin dragonfly freebsd !android,linux netbsd openbsd solaris -// +build cgo -// +build !osusergo package user diff --git a/libgo/go/os/user/decls_solaris.go b/libgo/go/os/user/decls_solaris.go index 43b6a044047..92c83328912 100644 --- a/libgo/go/os/user/decls_solaris.go +++ b/libgo/go/os/user/decls_solaris.go @@ -22,6 +22,3 @@ func libc_getgrnam_r(name *byte, grp *syscall.Group, buf *byte, buflen syscall.S //extern __posix_getgrgid_r func libc_getgrgid_r(gid syscall.Gid_t, grp *syscall.Group, buf *byte, buflen syscall.Size_t, result **syscall.Group) int - -//extern getgrouplist -func libc_getgrouplist(user *byte, group syscall.Gid_t, groups *syscall.Gid_t, ngroups *int32) int diff --git a/libgo/go/os/user/getgrouplist_unix.go b/libgo/go/os/user/getgrouplist_unix.go new file mode 100644 index 00000000000..527d941308e --- /dev/null +++ b/libgo/go/os/user/getgrouplist_unix.go @@ -0,0 +1,30 @@ +// 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. + +//go:build (dragonfly || freebsd || (!android && linux) || netbsd || openbsd || (solaris && !illumos)) && cgo && !osusergo + +package user + +import ( + "fmt" + "syscall" +) + +//extern-sysinfo getgrouplist +func getGroupList(name *byte, userGID syscall.Gid_t, gids *syscall.Gid_t, n *int32) int32 + +// groupRetry retries getGroupList with much larger size for n. The result is +// stored in gids. +func groupRetry(username string, name []byte, userGID syscall.Gid_t, gids *[]syscall.Gid_t, n *int32) error { + // More than initial buffer, but now n contains the correct size. + if *n > maxGroups { + return fmt.Errorf("user: %q is a member of more than %d groups", username, maxGroups) + } + *gids = make([]syscall.Gid_t, *n) + rv := getGroupList(&name[0], userGID, &(*gids)[0], n) + if rv == -1 { + return fmt.Errorf("user: list groups for %s failed", username) + } + return nil +} diff --git a/libgo/go/os/user/listgroups_aix.go b/libgo/go/os/user/listgroups_aix.go deleted file mode 100644 index 93feb80924c..00000000000 --- a/libgo/go/os/user/listgroups_aix.go +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright 2017 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. - -//go:build cgo && !osusergo -// +build cgo,!osusergo - -package user - -import "fmt" - -func listGroups(u *User) ([]string, error) { - return nil, fmt.Errorf("user: list groups for %s: not supported on AIX", u.Username) -} diff --git a/libgo/go/os/user/listgroups_illumos.go b/libgo/go/os/user/listgroups_illumos.go deleted file mode 100644 index d25e0339b99..00000000000 --- a/libgo/go/os/user/listgroups_illumos.go +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2021 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. - -//go:build cgo && !osusergo -// +build cgo,!osusergo - -// Even though this file requires no C, it is used to provide a -// listGroup stub because all the other illumos calls work. Otherwise, -// this stub will conflict with the lookup_stubs.go fallback. - -package user - -import "fmt" - -func listGroups(u *User) ([]string, error) { - return nil, fmt.Errorf("user: list groups for %s: not supported on illumos", u.Username) -} diff --git a/libgo/go/os/user/listgroups_stub.go b/libgo/go/os/user/listgroups_stub.go new file mode 100644 index 00000000000..4cf808b65de --- /dev/null +++ b/libgo/go/os/user/listgroups_stub.go @@ -0,0 +1,19 @@ +// Copyright 2021 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. + +//go:build android || (js && !wasm) + +package user + +import ( + "errors" +) + +func init() { + groupListImplemented = false +} + +func listGroups(*User) ([]string, error) { + return nil, errors.New("user: list groups not implemented") +} diff --git a/libgo/go/os/user/listgroups_unix.go b/libgo/go/os/user/listgroups_unix.go index 38bf807f52a..b3cf839b3ec 100644 --- a/libgo/go/os/user/listgroups_unix.go +++ b/libgo/go/os/user/listgroups_unix.go @@ -1,61 +1,114 @@ -// Copyright 2016 The Go Authors. All rights reserved. +// Copyright 2021 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. -//go:build (dragonfly || darwin || freebsd || hurd || (!android && linux) || netbsd || openbsd || (solaris && !illumos)) && cgo && !osusergo -// +build dragonfly darwin freebsd hurd !android,linux netbsd openbsd solaris,!illumos -// +build cgo -// +build !osusergo +//go:build ((darwin || dragonfly || freebsd || hurd || (js && wasm) || (!android && linux) || netbsd || openbsd || solaris) && (!cgo || osusergo)) || aix || illumos package user import ( + "bufio" + "bytes" + "errors" "fmt" + "io" + "os" "strconv" "syscall" ) -/* -#include <unistd.h> -#include <sys/types.h> -*/ +const groupFile = "/etc/group" -const maxGroups = 2048 +var colon = []byte{':'} -func listGroups(u *User) ([]string, error) { - ug, err := strconv.Atoi(u.Gid) - if err != nil { - return nil, fmt.Errorf("user: list groups for %s: invalid gid %q", u.Username, u.Gid) +func listGroupsFromReader(u *User, r io.Reader) ([]string, error) { + if u.Username == "" { + return nil, errors.New("user: list groups: empty username") } - userGID := syscall.Gid_t(ug) - nameC, err := syscall.BytePtrFromString(u.Username) + primaryGid, err := strconv.Atoi(u.Gid) if err != nil { - return nil, fmt.Errorf("user: invalid user name %q: %v", strconv.Quote(u.Username), err) + return nil, fmt.Errorf("user: list groups for %s: invalid gid %q", u.Username, u.Gid) } - n := int32(256) - gidsC := make([]syscall.Gid_t, n) - syscall.Entersyscall() - rv := libc_getgrouplist(nameC, userGID, &gidsC[0], &n) - syscall.Exitsyscall() - if rv == -1 { - // More than initial buffer, but now n contains the correct size. - const maxGroups = 2048 - if n > maxGroups { - return nil, fmt.Errorf("user: list groups for %s: member of more than %d groups", u.Username, maxGroups) + userCommas := []byte("," + u.Username + ",") // ,john, + userFirst := userCommas[1:] // john, + userLast := userCommas[:len(userCommas)-1] // ,john + userOnly := userCommas[1 : len(userCommas)-1] // john + + // Add primary Gid first. + groups := []string{u.Gid} + + rd := bufio.NewReader(r) + done := false + for !done { + line, err := rd.ReadBytes('\n') + if err != nil { + if err == io.EOF { + done = true + } else { + return groups, err + } + } + + // Look for username in the list of users. If user is found, + // append the GID to the groups slice. + + // There's no spec for /etc/passwd or /etc/group, but we try to follow + // the same rules as the glibc parser, which allows comments and blank + // space at the beginning of a line. + line = bytes.TrimSpace(line) + if len(line) == 0 || line[0] == '#' || + // If you search for a gid in a row where the group + // name (the first field) starts with "+" or "-", + // glibc fails to find the record, and so should we. + line[0] == '+' || line[0] == '-' { + continue + } + + // Format of /etc/group is + // groupname:password:GID:user_list + // for example + // wheel:x:10:john,paul,jack + // tcpdump:x:72: + listIdx := bytes.LastIndexByte(line, ':') + if listIdx == -1 || listIdx == len(line)-1 { + // No commas, or empty group list. + continue } - gidsC = make([]syscall.Gid_t, n) - syscall.Entersyscall() - rv := libc_getgrouplist(nameC, userGID, &gidsC[0], &n) - syscall.Exitsyscall() - if rv == -1 { - return nil, fmt.Errorf("user: list groups for %s failed (changed groups?)", u.Username) + if bytes.Count(line[:listIdx], colon) != 2 { + // Incorrect number of colons. + continue } + list := line[listIdx+1:] + // Check the list for user without splitting or copying. + if !(bytes.Equal(list, userOnly) || bytes.HasPrefix(list, userFirst) || bytes.HasSuffix(list, userLast) || bytes.Contains(list, userCommas)) { + continue + } + + // groupname:password:GID + parts := bytes.Split(line[:listIdx], colon) + if len(parts) != 3 || len(parts[0]) == 0 { + continue + } + gid := string(parts[2]) + // Make sure it's numeric and not the same as primary GID. + numGid, err := strconv.Atoi(gid) + if err != nil || numGid == primaryGid { + continue + } + + groups = append(groups, gid) } - gidsC = gidsC[:n] - gids := make([]string, 0, n) - for _, g := range gidsC[:n] { - gids = append(gids, strconv.Itoa(int(g))) + + return groups, nil +} + +func listGroups(u *User) ([]string, error) { + f, err := os.Open(groupFile) + if err != nil { + return nil, err } - return gids, nil + defer f.Close() + + return listGroupsFromReader(u, f) } diff --git a/libgo/go/os/user/listgroups_unix_test.go b/libgo/go/os/user/listgroups_unix_test.go new file mode 100644 index 00000000000..a9f79ec6bb0 --- /dev/null +++ b/libgo/go/os/user/listgroups_unix_test.go @@ -0,0 +1,107 @@ +// Copyright 2021 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. + +//go:build ((darwin || dragonfly || freebsd || (js && wasm) || (!android && linux) || netbsd || openbsd || solaris) && (!cgo || osusergo)) || aix || illumos + +package user + +import ( + "fmt" + "sort" + "strings" + "testing" +) + +var testGroupFile = `# See the opendirectoryd(8) man page for additional +# information about Open Directory. +## +nobody:*:-2: +nogroup:*:-1: +wheel:*:0:root +emptyid:*::root +invalidgid:*:notanumber:root ++plussign:*:20:root +-minussign:*:21:root +# Next line is invalid (empty group name) +:*:22:root + +daemon:*:1:root + indented:*:7:root +# comment:*:4:found + # comment:*:4:found +kmem:*:2:root +manymembers:x:777:jill,jody,john,jack,jov,user777 +` + largeGroup() + +func largeGroup() (res string) { + var b strings.Builder + b.WriteString("largegroup:x:1000:user1") + for i := 2; i <= 7500; i++ { + fmt.Fprintf(&b, ",user%d", i) + } + return b.String() +} + +var listGroupsTests = []struct { + // input + in string + user string + gid string + // output + gids []string + err bool +}{ + {in: testGroupFile, user: "root", gid: "0", gids: []string{"0", "1", "2", "7"}}, + {in: testGroupFile, user: "jill", gid: "33", gids: []string{"33", "777"}}, + {in: testGroupFile, user: "jody", gid: "34", gids: []string{"34", "777"}}, + {in: testGroupFile, user: "john", gid: "35", gids: []string{"35", "777"}}, + {in: testGroupFile, user: "jov", gid: "37", gids: []string{"37", "777"}}, + {in: testGroupFile, user: "user777", gid: "7", gids: []string{"7", "777", "1000"}}, + {in: testGroupFile, user: "user1111", gid: "1111", gids: []string{"1111", "1000"}}, + {in: testGroupFile, user: "user1000", gid: "1000", gids: []string{"1000"}}, + {in: testGroupFile, user: "user7500", gid: "7500", gids: []string{"1000", "7500"}}, + {in: testGroupFile, user: "no-such-user", gid: "2345", gids: []string{"2345"}}, + {in: "", user: "no-such-user", gid: "2345", gids: []string{"2345"}}, + // Error cases. + {in: "", user: "", gid: "2345", err: true}, + {in: "", user: "joanna", gid: "bad", err: true}, +} + +func TestListGroups(t *testing.T) { + for _, tc := range listGroupsTests { + u := &User{Username: tc.user, Gid: tc.gid} + got, err := listGroupsFromReader(u, strings.NewReader(tc.in)) + if tc.err { + if err == nil { + t.Errorf("listGroups(%q): got nil; want error", tc.user) + } + continue // no more checks + } + if err != nil { + t.Errorf("listGroups(%q): got %v error, want nil", tc.user, err) + continue // no more checks + } + checkSameIDs(t, got, tc.gids) + } +} + +func checkSameIDs(t *testing.T, got, want []string) { + t.Helper() + if len(got) != len(want) { + t.Errorf("ID list mismatch: got %v; want %v", got, want) + return + } + sort.Strings(got) + sort.Strings(want) + mismatch := -1 + for i, g := range want { + if got[i] != g { + mismatch = i + break + } + } + if mismatch != -1 { + t.Errorf("ID list mismatch (at index %d): got %v; want %v", mismatch, got, want) + } +} diff --git a/libgo/go/os/user/lookup_android.go b/libgo/go/os/user/lookup_android.go index 151aab49c20..0ae31fd8189 100644 --- a/libgo/go/os/user/lookup_android.go +++ b/libgo/go/os/user/lookup_android.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build android -// +build android package user diff --git a/libgo/go/os/user/lookup_plan9.go b/libgo/go/os/user/lookup_plan9.go index 33ae3a6adf4..07939363e73 100644 --- a/libgo/go/os/user/lookup_plan9.go +++ b/libgo/go/os/user/lookup_plan9.go @@ -18,7 +18,9 @@ const ( ) func init() { + userImplemented = false groupImplemented = false + groupListImplemented = false } func current() (*User, error) { diff --git a/libgo/go/os/user/lookup_stubs.go b/libgo/go/os/user/lookup_stubs.go index c975a11964c..ce1617d2507 100644 --- a/libgo/go/os/user/lookup_stubs.go +++ b/libgo/go/os/user/lookup_stubs.go @@ -3,22 +3,16 @@ // license that can be found in the LICENSE file. //go:build (!cgo && !windows && !plan9) || android || (osusergo && !windows && !plan9) -// +build !cgo,!windows,!plan9 android osusergo,!windows,!plan9 package user import ( - "errors" "fmt" "os" "runtime" "strconv" ) -func init() { - groupImplemented = false -} - func current() (*User, error) { uid := currentUID() // $USER and /etc/passwd may disagree; prefer the latter if we can get it. @@ -64,13 +58,6 @@ func current() (*User, error) { return u, fmt.Errorf("user: Current requires cgo or %s set in environment", missing) } -func listGroups(*User) ([]string, error) { - if runtime.GOOS == "android" || runtime.GOOS == "aix" { - return nil, fmt.Errorf("user: GroupIds not implemented on %s", runtime.GOOS) - } - return nil, errors.New("user: GroupIds requires cgo") -} - func currentUID() string { if id := os.Getuid(); id >= 0 { return strconv.Itoa(id) diff --git a/libgo/go/os/user/lookup_unix.go b/libgo/go/os/user/lookup_unix.go index 28b96340e15..090fad20e30 100644 --- a/libgo/go/os/user/lookup_unix.go +++ b/libgo/go/os/user/lookup_unix.go @@ -3,8 +3,6 @@ // license that can be found in the LICENSE file. //go:build (aix || darwin || dragonfly || freebsd || hurd || (js && wasm) || (!android && linux) || netbsd || openbsd || solaris) && (!cgo || osusergo) -// +build aix darwin dragonfly freebsd hurd js,wasm !android,linux netbsd openbsd solaris -// +build !cgo osusergo package user @@ -18,17 +16,10 @@ import ( "strings" ) -const groupFile = "/etc/group" const userFile = "/etc/passwd" -var colon = []byte{':'} - -func init() { - groupImplemented = false -} - // lineFunc returns a value, an error, or (nil, nil) to skip the row. -type lineFunc func(line []byte) (v interface{}, err error) +type lineFunc func(line []byte) (v any, err error) // readColonFile parses r as an /etc/group or /etc/passwd style file, running // fn for each row. readColonFile returns a value, an error, or (nil, nil) if @@ -36,7 +27,7 @@ type lineFunc func(line []byte) (v interface{}, err error) // // readCols is the minimum number of colon-separated fields that will be passed // to fn; in a long line additional fields may be silently discarded. -func readColonFile(r io.Reader, fn lineFunc, readCols int) (v interface{}, err error) { +func readColonFile(r io.Reader, fn lineFunc, readCols int) (v any, err error) { rd := bufio.NewReader(r) // Read the file line-by-line. @@ -107,7 +98,7 @@ func matchGroupIndexValue(value string, idx int) lineFunc { leadColon = ":" } substr := []byte(leadColon + value + ":") - return func(line []byte) (v interface{}, err error) { + return func(line []byte) (v any, err error) { if !bytes.Contains(line, substr) || bytes.Count(line, colon) < 3 { return } @@ -154,7 +145,7 @@ func matchUserIndexValue(value string, idx int) lineFunc { leadColon = ":" } substr := []byte(leadColon + value + ":") - return func(line []byte) (v interface{}, err error) { + return func(line []byte) (v any, err error) { if !bytes.Contains(line, substr) || bytes.Count(line, colon) < 6 { return } @@ -181,9 +172,7 @@ func matchUserIndexValue(value string, idx int) lineFunc { // say: "It is expected to be a comma separated list of // personal data where the first item is the full name of the // user." - if i := strings.Index(u.Name, ","); i >= 0 { - u.Name = u.Name[:i] - } + u.Name, _, _ = strings.Cut(u.Name, ",") return u, nil } } diff --git a/libgo/go/os/user/lookup_unix_test.go b/libgo/go/os/user/lookup_unix_test.go index 060cfe186f5..77917677fc1 100644 --- a/libgo/go/os/user/lookup_unix_test.go +++ b/libgo/go/os/user/lookup_unix_test.go @@ -3,36 +3,15 @@ // license that can be found in the LICENSE file. //go:build (aix || darwin || dragonfly || freebsd || (!android && linux) || netbsd || openbsd || solaris) && !cgo -// +build aix darwin dragonfly freebsd !android,linux netbsd openbsd solaris -// +build !cgo package user import ( - "fmt" "reflect" "strings" "testing" ) -var testGroupFile = `# See the opendirectoryd(8) man page for additional -# information about Open Directory. -## -nobody:*:-2: -nogroup:*:-1: -wheel:*:0:root -emptyid:*::root -invalidgid:*:notanumber:root -+plussign:*:20:root --minussign:*:21:root - -daemon:*:1:root - indented:*:7: -# comment:*:4:found - # comment:*:4:found -kmem:*:2:root -` + largeGroup() - var groupTests = []struct { in string name string @@ -51,19 +30,10 @@ var groupTests = []struct { {testGroupFile, "indented", "7"}, {testGroupFile, "# comment", ""}, {testGroupFile, "largegroup", "1000"}, + {testGroupFile, "manymembers", "777"}, {"", "emptyfile", ""}, } -// Generate a proper "largegroup" entry for testGroupFile string -func largeGroup() (res string) { - var b strings.Builder - b.WriteString("largegroup:x:1000:user1") - for i := 2; i <= 7500; i++ { - fmt.Fprintf(&b, ",user%d", i) - } - return b.String() -} - func TestFindGroupName(t *testing.T) { for _, tt := range groupTests { got, err := findGroupName(tt.name, strings.NewReader(tt.in)) diff --git a/libgo/go/os/user/user.go b/libgo/go/os/user/user.go index c1b8101c862..0307d2ad6a1 100644 --- a/libgo/go/os/user/user.go +++ b/libgo/go/os/user/user.go @@ -6,11 +6,13 @@ Package user allows user account lookups by name or id. For most Unix systems, this package has two internal implementations of -resolving user and group ids to names. One is written in pure Go and -parses /etc/passwd and /etc/group. The other is cgo-based and relies on -the standard C library (libc) routines such as getpwuid_r and getgrnam_r. +resolving user and group ids to names, and listing supplementary group IDs. +One is written in pure Go and parses /etc/passwd and /etc/group. The other +is cgo-based and relies on the standard C library (libc) routines such as +getpwuid_r, getgrnam_r, and getgrouplist. -When cgo is available, cgo-based (libc-backed) code is used by default. +When cgo is available, and the required routines are implemented in libc +for a particular platform, cgo-based (libc-backed) code is used. This can be overridden by using osusergo build tag, which enforces the pure Go implementation. */ @@ -20,9 +22,12 @@ import ( "strconv" ) +// These may be set to false in init() for a particular platform and/or +// build flags to let the tests know to skip tests of some features. var ( - userImplemented = true // set to false by lookup_stubs.go's init - groupImplemented = true // set to false by lookup_stubs.go's init + userImplemented = true + groupImplemented = true + groupListImplemented = true ) // User represents a user account. diff --git a/libgo/go/os/user/user_test.go b/libgo/go/os/user/user_test.go index 49920317bed..80251749a7d 100644 --- a/libgo/go/os/user/user_test.go +++ b/libgo/go/os/user/user_test.go @@ -5,7 +5,6 @@ package user import ( - "runtime" "testing" ) @@ -56,10 +55,6 @@ func compare(t *testing.T, want, got *User) { func TestLookup(t *testing.T) { checkUser(t) - if runtime.GOOS == "plan9" { - t.Skipf("Lookup not implemented on %q", runtime.GOOS) - } - want, err := Current() if err != nil { t.Fatalf("Current: %v", err) @@ -77,10 +72,6 @@ func TestLookup(t *testing.T) { func TestLookupId(t *testing.T) { checkUser(t) - if runtime.GOOS == "plan9" { - t.Skipf("LookupId not implemented on %q", runtime.GOOS) - } - want, err := Current() if err != nil { t.Fatalf("Current: %v", err) @@ -127,14 +118,15 @@ func TestLookupGroup(t *testing.T) { } } -func TestGroupIds(t *testing.T) { - checkGroup(t) - if runtime.GOOS == "aix" { - t.Skip("skipping GroupIds, see golang.org/issue/30563") - } - if runtime.GOOS == "illumos" { - t.Skip("skipping GroupIds, see golang.org/issue/14709") +func checkGroupList(t *testing.T) { + t.Helper() + if !groupListImplemented { + t.Skip("user: group list not implemented; skipping test") } +} + +func TestGroupIds(t *testing.T) { + checkGroupList(t) user, err := Current() if err != nil { t.Fatalf("Current(): %v", err) diff --git a/libgo/go/os/wait_unimp.go b/libgo/go/os/wait_unimp.go index 783a66f77e0..b11bbc04c75 100644 --- a/libgo/go/os/wait_unimp.go +++ b/libgo/go/os/wait_unimp.go @@ -2,8 +2,11 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build aix || darwin || hurd || (js && wasm) || openbsd || solaris -// +build aix darwin hurd js,wasm openbsd solaris +// aix, darwin, js/wasm, openbsd and solaris don't implement +// waitid/wait6. netbsd implements wait6, but that is causing test +// failures, see issue #48789. + +//go:build aix || darwin || hurd || (js && wasm) || netbsd || openbsd || solaris package os @@ -11,7 +14,9 @@ package os // succeed immediately, and reports whether it has done so. // It does not actually call p.Wait. // This version is used on systems that do not implement waitid, -// or where we have not implemented it yet. +// or where we have not implemented it yet. Note that this is racy: +// a call to Process.Signal can in an extremely unlikely case send a +// signal to the wrong process, see issue #13987. func (p *Process) blockUntilWaitable() (bool, error) { return false, nil } diff --git a/libgo/go/os/wait_wait6.go b/libgo/go/os/wait_wait6.go index 45b370a802e..d395dac40b4 100644 --- a/libgo/go/os/wait_wait6.go +++ b/libgo/go/os/wait_wait6.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build dragonfly || freebsd || netbsd -// +build dragonfly freebsd netbsd +//go:build dragonfly || freebsd package os diff --git a/libgo/go/os/wait_waitid.go b/libgo/go/os/wait_waitid.go index d7c97518b7a..31f4e77d38c 100644 --- a/libgo/go/os/wait_waitid.go +++ b/libgo/go/os/wait_waitid.go @@ -6,7 +6,6 @@ // waitid returns if the process is stopped, even when using WEXITED. //go:build linux -// +build linux package os |