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
18 // bytePtrToString takes a NUL-terminated array of bytes and convert
20 func bytePtrToString(p
*byte) string {
24 a
:= (*[10000]byte)(unsafe
.Pointer(p
))
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
)
44 err
:= retryWithBuffer(buf
, func() syscall
.Errno
{
45 syscall
.Entersyscall()
46 rv
:= libc_getpwnam_r(p
,
53 return syscall
.GetErrno()
58 return nil, fmt
.Errorf("user: lookup username %s: %v", username
, err
)
61 return nil, UnknownUserError(username
)
63 return buildUser(&pwd
), err
66 func lookupUserId(uid
string) (*User
, error
) {
67 i
, e
:= strconv
.Atoi(uid
)
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
)
81 err
:= retryWithBuffer(buf
, func() syscall
.Errno
{
82 syscall
.Entersyscall()
83 rv
:= libc_getpwuid_r(syscall
.Uid_t(uid
),
90 return syscall
.GetErrno()
95 return nil, fmt
.Errorf("user: lookup userid %d: %v", uid
, err
)
98 return nil, UnknownUserIdError(uid
)
100 return buildUser(&pwd
), nil
103 func buildUser(pwd
*syscall
.Passwd
) *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
115 if i
:= strings
.Index(u
.Name
, ","); i
>= 0 {
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
)
131 p
:= syscall
.StringBytePtr(groupname
)
133 err
:= retryWithBuffer(buf
, func() syscall
.Errno
{
134 syscall
.Entersyscall()
135 rv
:= libc_getgrnam_r(p
,
140 syscall
.Exitsyscall()
142 return syscall
.GetErrno()
147 return nil, fmt
.Errorf("user: lookup groupname %s: %v", groupname
, err
)
150 return nil, UnknownGroupError(groupname
)
152 return buildGroup(&grp
), nil
155 func lookupGroupId(gid
string) (*Group
, error
) {
156 i
, e
:= strconv
.Atoi(gid
)
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
)
170 err
:= retryWithBuffer(buf
, func() syscall
.Errno
{
171 syscall
.Entersyscall()
172 rv
:= libc_getgrgid_r(syscall
.Gid_t(gid
),
177 syscall
.Exitsyscall()
179 return syscall
.GetErrno()
184 return nil, fmt
.Errorf("user: lookup groupid %d: %v", gid
, err
)
187 return nil, UnknownGroupIdError(strconv
.Itoa(gid
))
189 return buildGroup(&grp
), nil
192 func buildGroup(grp
*syscall
.Group
) *Group
{
194 Gid
: strconv
.Itoa(int(grp
.Gr_gid
)),
195 Name
: bytePtrToString((*byte)(unsafe
.Pointer(grp
.Gr_name
))),
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
))
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.
215 if !isSizeReasonable(int64(sz
)) {
216 // Truncate. If this truly isn't enough, retryWithBuffer will error on the first run.
219 return syscall
.Size_t(sz
)
222 type memBuffer
struct {
227 func alloc(kind bufferKind
) *memBuffer
{
228 sz
:= kind
.initialSize()
229 b
:= make([]byte, sz
)
236 func (mb
*memBuffer
) resize(newSize syscall
.Size_t
) {
237 b
:= make([]byte, newSize
)
242 func (mb
*memBuffer
) free() {
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
{
254 } else if errno
!= syscall
.ERANGE
{
257 newSize
:= buf
.size
* 2
258 if !isSizeReasonable(int64(newSize
)) {
259 return fmt
.Errorf("internal buffer exceeds %d bytes", maxBufferSize
)
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