aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/go/_std_1.21/src/os
diff options
context:
space:
mode:
authornkozlovskiy <nmk@ydb.tech>2023-12-06 16:48:03 +0300
committernkozlovskiy <nmk@ydb.tech>2023-12-06 18:52:21 +0300
commit0856c44f0dac5a4dede7c44c16ba411f13b573ae (patch)
treea4b0833f45ffada5db15b14cb0b02dd20f747eb0 /contrib/go/_std_1.21/src/os
parent86f8b110c17767a66b011d38c1488230b41a1738 (diff)
downloadydb-0856c44f0dac5a4dede7c44c16ba411f13b573ae.tar.gz
ydb-oss: add some go depends
Diffstat (limited to 'contrib/go/_std_1.21/src/os')
-rw-r--r--contrib/go/_std_1.21/src/os/user/listgroups_unix.go109
-rw-r--r--contrib/go/_std_1.21/src/os/user/lookup_stubs.go83
-rw-r--r--contrib/go/_std_1.21/src/os/user/lookup_unix.go234
3 files changed, 426 insertions, 0 deletions
diff --git a/contrib/go/_std_1.21/src/os/user/listgroups_unix.go b/contrib/go/_std_1.21/src/os/user/listgroups_unix.go
new file mode 100644
index 0000000000..67bd8a776e
--- /dev/null
+++ b/contrib/go/_std_1.21/src/os/user/listgroups_unix.go
@@ -0,0 +1,109 @@
+// 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) || wasip1 || (!android && linux) || netbsd || openbsd || solaris) && ((!cgo && !darwin) || osusergo)) || aix || illumos
+
+package user
+
+import (
+ "bufio"
+ "bytes"
+ "errors"
+ "fmt"
+ "io"
+ "os"
+ "strconv"
+)
+
+func listGroupsFromReader(u *User, r io.Reader) ([]string, error) {
+ if u.Username == "" {
+ return nil, errors.New("user: list groups: empty username")
+ }
+ primaryGid, err := strconv.Atoi(u.Gid)
+ if err != nil {
+ return nil, fmt.Errorf("user: list groups for %s: invalid gid %q", u.Username, u.Gid)
+ }
+
+ 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
+ }
+ 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)
+ }
+
+ return groups, nil
+}
+
+func listGroups(u *User) ([]string, error) {
+ f, err := os.Open(groupFile)
+ if err != nil {
+ return nil, err
+ }
+ defer f.Close()
+
+ return listGroupsFromReader(u, f)
+}
diff --git a/contrib/go/_std_1.21/src/os/user/lookup_stubs.go b/contrib/go/_std_1.21/src/os/user/lookup_stubs.go
new file mode 100644
index 0000000000..89dfe455b5
--- /dev/null
+++ b/contrib/go/_std_1.21/src/os/user/lookup_stubs.go
@@ -0,0 +1,83 @@
+// Copyright 2011 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 && !darwin && !windows && !plan9) || android || (osusergo && !windows && !plan9)
+
+package user
+
+import (
+ "fmt"
+ "os"
+ "runtime"
+ "strconv"
+)
+
+var (
+ // unused variables (in this implementation)
+ // modified during test to exercise code paths in the cgo implementation.
+ userBuffer = 0
+ groupBuffer = 0
+)
+
+func current() (*User, error) {
+ uid := currentUID()
+ // $USER and /etc/passwd may disagree; prefer the latter if we can get it.
+ // See issue 27524 for more information.
+ u, err := lookupUserId(uid)
+ if err == nil {
+ return u, nil
+ }
+
+ homeDir, _ := os.UserHomeDir()
+ u = &User{
+ Uid: uid,
+ Gid: currentGID(),
+ Username: os.Getenv("USER"),
+ Name: "", // ignored
+ HomeDir: homeDir,
+ }
+ // On Android, return a dummy user instead of failing.
+ switch runtime.GOOS {
+ case "android":
+ if u.Uid == "" {
+ u.Uid = "1"
+ }
+ if u.Username == "" {
+ u.Username = "android"
+ }
+ }
+ // cgo isn't available, but if we found the minimum information
+ // without it, use it:
+ if u.Uid != "" && u.Username != "" && u.HomeDir != "" {
+ return u, nil
+ }
+ var missing string
+ if u.Username == "" {
+ missing = "$USER"
+ }
+ if u.HomeDir == "" {
+ if missing != "" {
+ missing += ", "
+ }
+ missing += "$HOME"
+ }
+ return u, fmt.Errorf("user: Current requires cgo or %s set in environment", missing)
+}
+
+func currentUID() string {
+ if id := os.Getuid(); id >= 0 {
+ return strconv.Itoa(id)
+ }
+ // Note: Windows returns -1, but this file isn't used on
+ // Windows anyway, so this empty return path shouldn't be
+ // used.
+ return ""
+}
+
+func currentGID() string {
+ if id := os.Getgid(); id >= 0 {
+ return strconv.Itoa(id)
+ }
+ return ""
+}
diff --git a/contrib/go/_std_1.21/src/os/user/lookup_unix.go b/contrib/go/_std_1.21/src/os/user/lookup_unix.go
new file mode 100644
index 0000000000..a4308269e0
--- /dev/null
+++ b/contrib/go/_std_1.21/src/os/user/lookup_unix.go
@@ -0,0 +1,234 @@
+// 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 ((unix && !android) || (js && wasm) || wasip1) && ((!cgo && !darwin) || osusergo)
+
+package user
+
+import (
+ "bufio"
+ "bytes"
+ "errors"
+ "io"
+ "os"
+ "strconv"
+ "strings"
+)
+
+// lineFunc returns a value, an error, or (nil, nil) to skip the row.
+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
+// the end of the file is reached without a match.
+//
+// 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 any, err error) {
+ rd := bufio.NewReader(r)
+
+ // Read the file line-by-line.
+ for {
+ var isPrefix bool
+ var wholeLine []byte
+
+ // Read the next line. We do so in chunks (as much as reader's
+ // buffer is able to keep), check if we read enough columns
+ // already on each step and store final result in wholeLine.
+ for {
+ var line []byte
+ line, isPrefix, err = rd.ReadLine()
+
+ if err != nil {
+ // We should return (nil, nil) if EOF is reached
+ // without a match.
+ if err == io.EOF {
+ err = nil
+ }
+ return nil, err
+ }
+
+ // Simple common case: line is short enough to fit in a
+ // single reader's buffer.
+ if !isPrefix && len(wholeLine) == 0 {
+ wholeLine = line
+ break
+ }
+
+ wholeLine = append(wholeLine, line...)
+
+ // Check if we read the whole line (or enough columns)
+ // already.
+ if !isPrefix || bytes.Count(wholeLine, []byte{':'}) >= readCols {
+ break
+ }
+ }
+
+ // 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.
+ wholeLine = bytes.TrimSpace(wholeLine)
+ if len(wholeLine) == 0 || wholeLine[0] == '#' {
+ continue
+ }
+ v, err = fn(wholeLine)
+ if v != nil || err != nil {
+ return
+ }
+
+ // If necessary, skip the rest of the line
+ for ; isPrefix; _, isPrefix, err = rd.ReadLine() {
+ if err != nil {
+ // We should return (nil, nil) if EOF is reached without a match.
+ if err == io.EOF {
+ err = nil
+ }
+ return nil, err
+ }
+ }
+ }
+}
+
+func matchGroupIndexValue(value string, idx int) lineFunc {
+ var leadColon string
+ if idx > 0 {
+ leadColon = ":"
+ }
+ substr := []byte(leadColon + value + ":")
+ return func(line []byte) (v any, err error) {
+ if !bytes.Contains(line, substr) || bytes.Count(line, colon) < 3 {
+ return
+ }
+ // wheel:*:0:root
+ parts := strings.SplitN(string(line), ":", 4)
+ if len(parts) < 4 || parts[0] == "" || parts[idx] != value ||
+ // If the file contains +foo and you search for "foo", glibc
+ // returns an "invalid argument" error. Similarly, if you search
+ // for a gid for a row where the group name starts with "+" or "-",
+ // glibc fails to find the record.
+ parts[0][0] == '+' || parts[0][0] == '-' {
+ return
+ }
+ if _, err := strconv.Atoi(parts[2]); err != nil {
+ return nil, nil
+ }
+ return &Group{Name: parts[0], Gid: parts[2]}, nil
+ }
+}
+
+func findGroupId(id string, r io.Reader) (*Group, error) {
+ if v, err := readColonFile(r, matchGroupIndexValue(id, 2), 3); err != nil {
+ return nil, err
+ } else if v != nil {
+ return v.(*Group), nil
+ }
+ return nil, UnknownGroupIdError(id)
+}
+
+func findGroupName(name string, r io.Reader) (*Group, error) {
+ if v, err := readColonFile(r, matchGroupIndexValue(name, 0), 3); err != nil {
+ return nil, err
+ } else if v != nil {
+ return v.(*Group), nil
+ }
+ return nil, UnknownGroupError(name)
+}
+
+// returns a *User for a row if that row's has the given value at the
+// given index.
+func matchUserIndexValue(value string, idx int) lineFunc {
+ var leadColon string
+ if idx > 0 {
+ leadColon = ":"
+ }
+ substr := []byte(leadColon + value + ":")
+ return func(line []byte) (v any, err error) {
+ if !bytes.Contains(line, substr) || bytes.Count(line, colon) < 6 {
+ return
+ }
+ // kevin:x:1005:1006::/home/kevin:/usr/bin/zsh
+ parts := strings.SplitN(string(line), ":", 7)
+ if len(parts) < 6 || parts[idx] != value || parts[0] == "" ||
+ parts[0][0] == '+' || parts[0][0] == '-' {
+ return
+ }
+ if _, err := strconv.Atoi(parts[2]); err != nil {
+ return nil, nil
+ }
+ if _, err := strconv.Atoi(parts[3]); err != nil {
+ return nil, nil
+ }
+ u := &User{
+ Username: parts[0],
+ Uid: parts[2],
+ Gid: parts[3],
+ Name: parts[4],
+ HomeDir: parts[5],
+ }
+ // The pw_gecos field isn't quite standardized. Some docs
+ // say: "It is expected to be a comma separated list of
+ // personal data where the first item is the full name of the
+ // user."
+ u.Name, _, _ = strings.Cut(u.Name, ",")
+ return u, nil
+ }
+}
+
+func findUserId(uid string, r io.Reader) (*User, error) {
+ i, e := strconv.Atoi(uid)
+ if e != nil {
+ return nil, errors.New("user: invalid userid " + uid)
+ }
+ if v, err := readColonFile(r, matchUserIndexValue(uid, 2), 6); err != nil {
+ return nil, err
+ } else if v != nil {
+ return v.(*User), nil
+ }
+ return nil, UnknownUserIdError(i)
+}
+
+func findUsername(name string, r io.Reader) (*User, error) {
+ if v, err := readColonFile(r, matchUserIndexValue(name, 0), 6); err != nil {
+ return nil, err
+ } else if v != nil {
+ return v.(*User), nil
+ }
+ return nil, UnknownUserError(name)
+}
+
+func lookupGroup(groupname string) (*Group, error) {
+ f, err := os.Open(groupFile)
+ if err != nil {
+ return nil, err
+ }
+ defer f.Close()
+ return findGroupName(groupname, f)
+}
+
+func lookupGroupId(id string) (*Group, error) {
+ f, err := os.Open(groupFile)
+ if err != nil {
+ return nil, err
+ }
+ defer f.Close()
+ return findGroupId(id, f)
+}
+
+func lookupUser(username string) (*User, error) {
+ f, err := os.Open(userFile)
+ if err != nil {
+ return nil, err
+ }
+ defer f.Close()
+ return findUsername(username, f)
+}
+
+func lookupUserId(uid string) (*User, error) {
+ f, err := os.Open(userFile)
+ if err != nil {
+ return nil, err
+ }
+ defer f.Close()
+ return findUserId(uid, f)
+}