libgo: update to Go 1.11
[official-gcc.git] / libgo / go / os / user / cgo_lookup_unix.go
blob722e95be3a4b5af91a15f9ce6c8eb0ca11d61f9f
1 // Copyright 2011 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
5 // +build aix darwin dragonfly freebsd !android,linux netbsd openbsd solaris
6 // +build cgo,!osusergo
8 package user
10 import (
11 "fmt"
12 "strconv"
13 "strings"
14 "syscall"
15 "unsafe"
18 // bytePtrToString takes a NUL-terminated array of bytes and convert
19 // it to a Go string.
20 func bytePtrToString(p *byte) string {
21 if p == nil {
22 return ""
24 a := (*[10000]byte)(unsafe.Pointer(p))
25 i := 0
26 for a[i] != 0 {
27 i++
29 return string(a[:i])
32 func current() (*User, error) {
33 return lookupUnixUid(syscall.Getuid())
36 func lookupUser(username string) (*User, error) {
37 var pwd syscall.Passwd
38 var result *syscall.Passwd
39 p := syscall.StringBytePtr(username)
41 buf := alloc(userBuffer)
42 defer buf.free()
44 err := retryWithBuffer(buf, func() syscall.Errno {
45 syscall.Entersyscall()
46 rv := libc_getpwnam_r(p,
47 &pwd,
48 buf.ptr,
49 buf.size,
50 &result)
51 syscall.Exitsyscall()
52 if rv != 0 {
53 return syscall.GetErrno()
55 return 0
57 if err != nil {
58 return nil, fmt.Errorf("user: lookup username %s: %v", username, err)
60 if result == nil {
61 return nil, UnknownUserError(username)
63 return buildUser(&pwd), err
66 func lookupUserId(uid string) (*User, error) {
67 i, e := strconv.Atoi(uid)
68 if e != nil {
69 return nil, e
71 return lookupUnixUid(i)
74 func lookupUnixUid(uid int) (*User, error) {
75 var pwd syscall.Passwd
76 var result *syscall.Passwd
78 buf := alloc(userBuffer)
79 defer buf.free()
81 err := retryWithBuffer(buf, func() syscall.Errno {
82 syscall.Entersyscall()
83 rv := libc_getpwuid_r(syscall.Uid_t(uid),
84 &pwd,
85 buf.ptr,
86 buf.size,
87 &result)
88 syscall.Exitsyscall()
89 if rv != 0 {
90 return syscall.GetErrno()
92 return 0
94 if err != nil {
95 return nil, fmt.Errorf("user: lookup userid %d: %v", uid, err)
97 if result == nil {
98 return nil, UnknownUserIdError(uid)
100 return buildUser(&pwd), nil
103 func buildUser(pwd *syscall.Passwd) *User {
104 u := &User{
105 Uid: strconv.FormatUint(uint64(pwd.Pw_uid), 10),
106 Gid: strconv.FormatUint(uint64(pwd.Pw_gid), 10),
107 Username: bytePtrToString((*byte)(unsafe.Pointer(pwd.Pw_name))),
108 Name: bytePtrToString((*byte)(unsafe.Pointer(pwd.Pw_gecos))),
109 HomeDir: bytePtrToString((*byte)(unsafe.Pointer(pwd.Pw_dir))),
111 // The pw_gecos field isn't quite standardized. Some docs
112 // say: "It is expected to be a comma separated list of
113 // personal data where the first item is the full name of the
114 // user."
115 if i := strings.Index(u.Name, ","); i >= 0 {
116 u.Name = u.Name[:i]
118 return u
121 func currentGroup() (*Group, error) {
122 return lookupUnixGid(syscall.Getgid())
125 func lookupGroup(groupname string) (*Group, error) {
126 var grp syscall.Group
127 var result *syscall.Group
129 buf := alloc(groupBuffer)
130 defer buf.free()
131 p := syscall.StringBytePtr(groupname)
133 err := retryWithBuffer(buf, func() syscall.Errno {
134 syscall.Entersyscall()
135 rv := libc_getgrnam_r(p,
136 &grp,
137 buf.ptr,
138 buf.size,
139 &result)
140 syscall.Exitsyscall()
141 if rv != 0 {
142 return syscall.GetErrno()
144 return 0
146 if err != nil {
147 return nil, fmt.Errorf("user: lookup groupname %s: %v", groupname, err)
149 if result == nil {
150 return nil, UnknownGroupError(groupname)
152 return buildGroup(&grp), nil
155 func lookupGroupId(gid string) (*Group, error) {
156 i, e := strconv.Atoi(gid)
157 if e != nil {
158 return nil, e
160 return lookupUnixGid(i)
163 func lookupUnixGid(gid int) (*Group, error) {
164 var grp syscall.Group
165 var result *syscall.Group
167 buf := alloc(groupBuffer)
168 defer buf.free()
170 err := retryWithBuffer(buf, func() syscall.Errno {
171 syscall.Entersyscall()
172 rv := libc_getgrgid_r(syscall.Gid_t(gid),
173 &grp,
174 buf.ptr,
175 buf.size,
176 &result)
177 syscall.Exitsyscall()
178 if rv != 0 {
179 return syscall.GetErrno()
181 return 0
183 if err != nil {
184 return nil, fmt.Errorf("user: lookup groupid %d: %v", gid, err)
186 if result == nil {
187 return nil, UnknownGroupIdError(strconv.Itoa(gid))
189 return buildGroup(&grp), nil
192 func buildGroup(grp *syscall.Group) *Group {
193 g := &Group{
194 Gid: strconv.Itoa(int(grp.Gr_gid)),
195 Name: bytePtrToString((*byte)(unsafe.Pointer(grp.Gr_name))),
197 return g
200 type bufferKind int
202 const (
203 userBuffer = bufferKind(syscall.SC_GETPW_R_SIZE_MAX)
204 groupBuffer = bufferKind(syscall.SC_GETGR_R_SIZE_MAX)
207 func (k bufferKind) initialSize() syscall.Size_t {
208 sz, _ := syscall.Sysconf(int(k))
209 if sz == -1 {
210 // DragonFly and FreeBSD do not have _SC_GETPW_R_SIZE_MAX.
211 // Additionally, not all Linux systems have it, either. For
212 // example, the musl libc returns -1.
213 return 1024
215 if !isSizeReasonable(int64(sz)) {
216 // Truncate. If this truly isn't enough, retryWithBuffer will error on the first run.
217 return maxBufferSize
219 return syscall.Size_t(sz)
222 type memBuffer struct {
223 ptr *byte
224 size syscall.Size_t
227 func alloc(kind bufferKind) *memBuffer {
228 sz := kind.initialSize()
229 b := make([]byte, sz)
230 return &memBuffer{
231 ptr: &b[0],
232 size: sz,
236 func (mb *memBuffer) resize(newSize syscall.Size_t) {
237 b := make([]byte, newSize)
238 mb.ptr = &b[0]
239 mb.size = newSize
242 func (mb *memBuffer) free() {
243 mb.ptr = nil
246 // retryWithBuffer repeatedly calls f(), increasing the size of the
247 // buffer each time, until f succeeds, fails with a non-ERANGE error,
248 // or the buffer exceeds a reasonable limit.
249 func retryWithBuffer(buf *memBuffer, f func() syscall.Errno) error {
250 for {
251 errno := f()
252 if errno == 0 {
253 return nil
254 } else if errno != syscall.ERANGE {
255 return errno
257 newSize := buf.size * 2
258 if !isSizeReasonable(int64(newSize)) {
259 return fmt.Errorf("internal buffer exceeds %d bytes", maxBufferSize)
261 buf.resize(newSize)
265 const maxBufferSize = 1 << 20
267 func isSizeReasonable(sz int64) bool {
268 return sz > 0 && sz <= maxBufferSize
271 // Because we can't use cgo in tests:
272 func structPasswdForNegativeTest() syscall.Passwd {
273 sp := syscall.Passwd{}
274 sp.Pw_uid = 1<<32 - 2
275 sp.Pw_gid = 1<<32 - 3
276 return sp