summaryrefslogtreecommitdiff
path: root/libgo/go/os
diff options
context:
space:
mode:
authorIan Lance Taylor <iant@golang.org>2022-02-11 14:53:56 -0800
committerIan Lance Taylor <iant@golang.org>2022-02-11 15:01:19 -0800
commit8dc2499aa62f768c6395c9754b8cabc1ce25c494 (patch)
tree43d7fd2bbfd7ad8c9625a718a5e8718889351994 /libgo/go/os
parent9a56779dbc4e2d9c15be8d31e36f2f59be7331a8 (diff)
downloadgcc-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')
-rw-r--r--libgo/go/os/env_test.go38
-rw-r--r--libgo/go/os/env_unix_test.go1
-rw-r--r--libgo/go/os/error_errno.go1
-rw-r--r--libgo/go/os/error_posix.go1
-rw-r--r--libgo/go/os/error_unix_test.go1
-rw-r--r--libgo/go/os/error_windows_test.go1
-rw-r--r--libgo/go/os/exec.go6
-rw-r--r--libgo/go/os/exec/exec.go12
-rw-r--r--libgo/go/os/exec/exec_linux_test.go1
-rw-r--r--libgo/go/os/exec/exec_posix_test.go1
-rw-r--r--libgo/go/os/exec/exec_test.go165
-rw-r--r--libgo/go/os/exec/exec_unix.go1
-rw-r--r--libgo/go/os/exec/exec_windows_test.go15
-rw-r--r--libgo/go/os/exec/internal/fdtest/exists_js.go18
-rw-r--r--libgo/go/os/exec/internal/fdtest/exists_plan9.go20
-rw-r--r--libgo/go/os/exec/internal/fdtest/exists_test.go21
-rw-r--r--libgo/go/os/exec/internal/fdtest/exists_unix.go19
-rw-r--r--libgo/go/os/exec/internal/fdtest/exists_windows.go12
-rw-r--r--libgo/go/os/exec/lp_js.go1
-rw-r--r--libgo/go/os/exec/lp_unix.go1
-rw-r--r--libgo/go/os/exec/lp_unix_test.go1
-rw-r--r--libgo/go/os/exec/read3.go93
-rw-r--r--libgo/go/os/exec_plan9.go4
-rw-r--r--libgo/go/os/exec_posix.go5
-rw-r--r--libgo/go/os/exec_unix.go1
-rw-r--r--libgo/go/os/exec_unix_test.go1
-rw-r--r--libgo/go/os/executable_path.go1
-rw-r--r--libgo/go/os/executable_plan9.go1
-rw-r--r--libgo/go/os/executable_procfs.go1
-rw-r--r--libgo/go/os/executable_sysctl.go1
-rw-r--r--libgo/go/os/export_unix_test.go1
-rw-r--r--libgo/go/os/fifo_test.go1
-rw-r--r--libgo/go/os/file.go4
-rw-r--r--libgo/go/os/file_plan9.go2
-rw-r--r--libgo/go/os/file_posix.go3
-rw-r--r--libgo/go/os/file_unix.go1
-rw-r--r--libgo/go/os/os_test.go122
-rw-r--r--libgo/go/os/os_unix_test.go1
-rw-r--r--libgo/go/os/path_test.go7
-rw-r--r--libgo/go/os/path_unix.go1
-rw-r--r--libgo/go/os/pipe2_bsd.go1
-rw-r--r--libgo/go/os/pipe2_illumos.go1
-rw-r--r--libgo/go/os/pipe_bsd.go1
-rw-r--r--libgo/go/os/pipe_test.go6
-rw-r--r--libgo/go/os/rawconn.go1
-rw-r--r--libgo/go/os/rawconn_test.go1
-rw-r--r--libgo/go/os/readfrom_stub.go1
-rw-r--r--libgo/go/os/removeall_at.go1
-rw-r--r--libgo/go/os/removeall_noat.go1
-rw-r--r--libgo/go/os/signal/example_unix_test.go1
-rw-r--r--libgo/go/os/signal/internal/pty/pty.go4
-rw-r--r--libgo/go/os/signal/signal_cgo_test.go2
-rw-r--r--libgo/go/os/signal/signal_linux_test.go1
-rw-r--r--libgo/go/os/signal/signal_test.go4
-rw-r--r--libgo/go/os/signal/signal_unix.go1
-rw-r--r--libgo/go/os/stat_js.go1
-rw-r--r--libgo/go/os/stat_plan9.go2
-rw-r--r--libgo/go/os/stat_unix.go1
-rw-r--r--libgo/go/os/sticky_bsd.go1
-rw-r--r--libgo/go/os/sticky_notbsd.go8
-rw-r--r--libgo/go/os/sys_bsd.go1
-rw-r--r--libgo/go/os/sys_js.go1
-rw-r--r--libgo/go/os/sys_unix.go1
-rw-r--r--libgo/go/os/timeout_test.go1
-rw-r--r--libgo/go/os/types_plan9.go4
-rw-r--r--libgo/go/os/types_unix.go3
-rw-r--r--libgo/go/os/types_windows.go2
-rw-r--r--libgo/go/os/user/cgo_listgroups_unix.go43
-rw-r--r--libgo/go/os/user/cgo_lookup_unix.go7
-rw-r--r--libgo/go/os/user/cgo_unix_test.go3
-rw-r--r--libgo/go/os/user/decls_solaris.go3
-rw-r--r--libgo/go/os/user/getgrouplist_unix.go30
-rw-r--r--libgo/go/os/user/listgroups_aix.go14
-rw-r--r--libgo/go/os/user/listgroups_illumos.go18
-rw-r--r--libgo/go/os/user/listgroups_stub.go19
-rw-r--r--libgo/go/os/user/listgroups_unix.go129
-rw-r--r--libgo/go/os/user/listgroups_unix_test.go107
-rw-r--r--libgo/go/os/user/lookup_android.go1
-rw-r--r--libgo/go/os/user/lookup_plan9.go2
-rw-r--r--libgo/go/os/user/lookup_stubs.go13
-rw-r--r--libgo/go/os/user/lookup_unix.go21
-rw-r--r--libgo/go/os/user/lookup_unix_test.go32
-rw-r--r--libgo/go/os/user/user.go17
-rw-r--r--libgo/go/os/user/user_test.go24
-rw-r--r--libgo/go/os/wait_unimp.go11
-rw-r--r--libgo/go/os/wait_wait6.go3
-rw-r--r--libgo/go/os/wait_waitid.go1
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