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