summaryrefslogtreecommitdiff
path: root/libgo/go/os/user
diff options
context:
space:
mode:
Diffstat (limited to 'libgo/go/os/user')
-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
17 files changed, 318 insertions, 165 deletions
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)