Source file
src/os/user/cgo_lookup_unix.go
Documentation: os/user
1
2
3
4
5
6
7
8
9
10 package user
11
12 import (
13 "fmt"
14 "strconv"
15 "strings"
16 "syscall"
17 "unsafe"
18 )
19
20
48 import "C"
49
50 func current() (*User, error) {
51 return lookupUnixUid(syscall.Getuid())
52 }
53
54 func lookupUser(username string) (*User, error) {
55 var pwd C.struct_passwd
56 var result *C.struct_passwd
57 nameC := make([]byte, len(username)+1)
58 copy(nameC, username)
59
60 buf := alloc(userBuffer)
61 defer buf.free()
62
63 err := retryWithBuffer(buf, func() syscall.Errno {
64
65
66
67
68 return syscall.Errno(C.mygetpwnam_r((*C.char)(unsafe.Pointer(&nameC[0])),
69 &pwd,
70 (*C.char)(buf.ptr),
71 C.size_t(buf.size),
72 &result))
73 })
74 if err != nil {
75 return nil, fmt.Errorf("user: lookup username %s: %v", username, err)
76 }
77 if result == nil {
78 return nil, UnknownUserError(username)
79 }
80 return buildUser(&pwd), err
81 }
82
83 func lookupUserId(uid string) (*User, error) {
84 i, e := strconv.Atoi(uid)
85 if e != nil {
86 return nil, e
87 }
88 return lookupUnixUid(i)
89 }
90
91 func lookupUnixUid(uid int) (*User, error) {
92 var pwd C.struct_passwd
93 var result *C.struct_passwd
94
95 buf := alloc(userBuffer)
96 defer buf.free()
97
98 err := retryWithBuffer(buf, func() syscall.Errno {
99
100
101 return syscall.Errno(C.mygetpwuid_r(C.int(uid),
102 &pwd,
103 (*C.char)(buf.ptr),
104 C.size_t(buf.size),
105 &result))
106 })
107 if err != nil {
108 return nil, fmt.Errorf("user: lookup userid %d: %v", uid, err)
109 }
110 if result == nil {
111 return nil, UnknownUserIdError(uid)
112 }
113 return buildUser(&pwd), nil
114 }
115
116 func buildUser(pwd *C.struct_passwd) *User {
117 u := &User{
118 Uid: strconv.FormatUint(uint64(pwd.pw_uid), 10),
119 Gid: strconv.FormatUint(uint64(pwd.pw_gid), 10),
120 Username: C.GoString(pwd.pw_name),
121 Name: C.GoString(pwd.pw_gecos),
122 HomeDir: C.GoString(pwd.pw_dir),
123 }
124
125
126
127
128 if i := strings.Index(u.Name, ","); i >= 0 {
129 u.Name = u.Name[:i]
130 }
131 return u
132 }
133
134 func lookupGroup(groupname string) (*Group, error) {
135 var grp C.struct_group
136 var result *C.struct_group
137
138 buf := alloc(groupBuffer)
139 defer buf.free()
140 cname := make([]byte, len(groupname)+1)
141 copy(cname, groupname)
142
143 err := retryWithBuffer(buf, func() syscall.Errno {
144 return syscall.Errno(C.mygetgrnam_r((*C.char)(unsafe.Pointer(&cname[0])),
145 &grp,
146 (*C.char)(buf.ptr),
147 C.size_t(buf.size),
148 &result))
149 })
150 if err != nil {
151 return nil, fmt.Errorf("user: lookup groupname %s: %v", groupname, err)
152 }
153 if result == nil {
154 return nil, UnknownGroupError(groupname)
155 }
156 return buildGroup(&grp), nil
157 }
158
159 func lookupGroupId(gid string) (*Group, error) {
160 i, e := strconv.Atoi(gid)
161 if e != nil {
162 return nil, e
163 }
164 return lookupUnixGid(i)
165 }
166
167 func lookupUnixGid(gid int) (*Group, error) {
168 var grp C.struct_group
169 var result *C.struct_group
170
171 buf := alloc(groupBuffer)
172 defer buf.free()
173
174 err := retryWithBuffer(buf, func() syscall.Errno {
175
176
177 return syscall.Errno(C.mygetgrgid_r(C.int(gid),
178 &grp,
179 (*C.char)(buf.ptr),
180 C.size_t(buf.size),
181 &result))
182 })
183 if err != nil {
184 return nil, fmt.Errorf("user: lookup groupid %d: %v", gid, err)
185 }
186 if result == nil {
187 return nil, UnknownGroupIdError(strconv.Itoa(gid))
188 }
189 return buildGroup(&grp), nil
190 }
191
192 func buildGroup(grp *C.struct_group) *Group {
193 g := &Group{
194 Gid: strconv.Itoa(int(grp.gr_gid)),
195 Name: C.GoString(grp.gr_name),
196 }
197 return g
198 }
199
200 type bufferKind C.int
201
202 const (
203 userBuffer = bufferKind(C._SC_GETPW_R_SIZE_MAX)
204 groupBuffer = bufferKind(C._SC_GETGR_R_SIZE_MAX)
205 )
206
207 func (k bufferKind) initialSize() C.size_t {
208 sz := C.sysconf(C.int(k))
209 if sz == -1 {
210
211
212
213 return 1024
214 }
215 if !isSizeReasonable(int64(sz)) {
216
217 return maxBufferSize
218 }
219 return C.size_t(sz)
220 }
221
222 type memBuffer struct {
223 ptr unsafe.Pointer
224 size C.size_t
225 }
226
227 func alloc(kind bufferKind) *memBuffer {
228 sz := kind.initialSize()
229 return &memBuffer{
230 ptr: C.malloc(sz),
231 size: sz,
232 }
233 }
234
235 func (mb *memBuffer) resize(newSize C.size_t) {
236 mb.ptr = C.realloc(mb.ptr, newSize)
237 mb.size = newSize
238 }
239
240 func (mb *memBuffer) free() {
241 C.free(mb.ptr)
242 }
243
244
245
246
247 func retryWithBuffer(buf *memBuffer, f func() syscall.Errno) error {
248 for {
249 errno := f()
250 if errno == 0 {
251 return nil
252 } else if errno != syscall.ERANGE {
253 return errno
254 }
255 newSize := buf.size * 2
256 if !isSizeReasonable(int64(newSize)) {
257 return fmt.Errorf("internal buffer exceeds %d bytes", maxBufferSize)
258 }
259 buf.resize(newSize)
260 }
261 }
262
263 const maxBufferSize = 1 << 20
264
265 func isSizeReasonable(sz int64) bool {
266 return sz > 0 && sz <= maxBufferSize
267 }
268
269
270 func structPasswdForNegativeTest() C.struct_passwd {
271 sp := C.struct_passwd{}
272 sp.pw_uid = 1<<32 - 2
273 sp.pw_gid = 1<<32 - 3
274 return sp
275 }
276
View as plain text