github.com/code-reading/golang@v0.0.0-20220303082512-ba5bc0e589a3/go/src/os/user/cgo_lookup_unix.go (about) 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. 4 5 //go:build (aix || darwin || dragonfly || freebsd || (!android && linux) || netbsd || openbsd || solaris) && cgo && !osusergo 6 // +build aix darwin dragonfly freebsd !android,linux netbsd openbsd solaris 7 // +build cgo 8 // +build !osusergo 9 10 package user 11 12 import ( 13 "fmt" 14 "strconv" 15 "strings" 16 "syscall" 17 "unsafe" 18 ) 19 20 /* 21 #cgo solaris CFLAGS: -D_POSIX_PTHREAD_SEMANTICS 22 #include <unistd.h> 23 #include <sys/types.h> 24 #include <pwd.h> 25 #include <grp.h> 26 #include <stdlib.h> 27 28 static int mygetpwuid_r(int uid, struct passwd *pwd, 29 char *buf, size_t buflen, struct passwd **result) { 30 return getpwuid_r(uid, pwd, buf, buflen, result); 31 } 32 33 static int mygetpwnam_r(const char *name, struct passwd *pwd, 34 char *buf, size_t buflen, struct passwd **result) { 35 return getpwnam_r(name, pwd, buf, buflen, result); 36 } 37 38 static int mygetgrgid_r(int gid, struct group *grp, 39 char *buf, size_t buflen, struct group **result) { 40 return getgrgid_r(gid, grp, buf, buflen, result); 41 } 42 43 static int mygetgrnam_r(const char *name, struct group *grp, 44 char *buf, size_t buflen, struct group **result) { 45 return getgrnam_r(name, grp, buf, buflen, result); 46 } 47 */ 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 // mygetpwnam_r is a wrapper around getpwnam_r to avoid 65 // passing a size_t to getpwnam_r, because for unknown 66 // reasons passing a size_t to getpwnam_r doesn't work on 67 // Solaris. 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 // mygetpwuid_r is a wrapper around getpwuid_r to avoid using uid_t 100 // because C.uid_t(uid) for unknown reasons doesn't work on linux. 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 // The pw_gecos field isn't quite standardized. Some docs 125 // say: "It is expected to be a comma separated list of 126 // personal data where the first item is the full name of the 127 // user." 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 // mygetgrgid_r is a wrapper around getgrgid_r to avoid using gid_t 176 // because C.gid_t(gid) for unknown reasons doesn't work on linux. 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 // 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 214 } 215 if !isSizeReasonable(int64(sz)) { 216 // Truncate. If this truly isn't enough, retryWithBuffer will error on the first run. 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 // retryWithBuffer repeatedly calls f(), increasing the size of the 245 // buffer each time, until f succeeds, fails with a non-ERANGE error, 246 // or the buffer exceeds a reasonable limit. 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 // Because we can't use cgo in tests: 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 }