diff options
Diffstat (limited to 'libgo/go/os/user')
-rw-r--r-- | libgo/go/os/user/cgo_listgroups_unix.go | 43 | ||||
-rw-r--r-- | libgo/go/os/user/cgo_lookup_unix.go | 7 | ||||
-rw-r--r-- | libgo/go/os/user/cgo_unix_test.go | 3 | ||||
-rw-r--r-- | libgo/go/os/user/decls_solaris.go | 3 | ||||
-rw-r--r-- | libgo/go/os/user/getgrouplist_unix.go | 30 | ||||
-rw-r--r-- | libgo/go/os/user/listgroups_aix.go | 14 | ||||
-rw-r--r-- | libgo/go/os/user/listgroups_illumos.go | 18 | ||||
-rw-r--r-- | libgo/go/os/user/listgroups_stub.go | 19 | ||||
-rw-r--r-- | libgo/go/os/user/listgroups_unix.go | 129 | ||||
-rw-r--r-- | libgo/go/os/user/listgroups_unix_test.go | 107 | ||||
-rw-r--r-- | libgo/go/os/user/lookup_android.go | 1 | ||||
-rw-r--r-- | libgo/go/os/user/lookup_plan9.go | 2 | ||||
-rw-r--r-- | libgo/go/os/user/lookup_stubs.go | 13 | ||||
-rw-r--r-- | libgo/go/os/user/lookup_unix.go | 21 | ||||
-rw-r--r-- | libgo/go/os/user/lookup_unix_test.go | 32 | ||||
-rw-r--r-- | libgo/go/os/user/user.go | 17 | ||||
-rw-r--r-- | libgo/go/os/user/user_test.go | 24 |
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) |