github.com/geraldss/go/src@v0.0.0-20210511222824-ac7d0ebfc235/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 // +build aix darwin dragonfly freebsd !android,linux netbsd openbsd solaris 6 // +build cgo,!osusergo 7 8 package user 9 10 import ( 11 "fmt" 12 "strconv" 13 "strings" 14 "syscall" 15 "unsafe" 16 ) 17 18 /* 19 #cgo solaris CFLAGS: -D_POSIX_PTHREAD_SEMANTICS 20 #include <unistd.h> 21 #include <sys/types.h> 22 #include <pwd.h> 23 #include <grp.h> 24 #include <stdlib.h> 25 26 static int mygetpwuid_r(int uid, struct passwd *pwd, 27 char *buf, size_t buflen, struct passwd **result) { 28 return getpwuid_r(uid, pwd, buf, buflen, result); 29 } 30 31 static int mygetpwnam_r(const char *name, struct passwd *pwd, 32 char *buf, size_t buflen, struct passwd **result) { 33 return getpwnam_r(name, pwd, buf, buflen, result); 34 } 35 36 static int mygetgrgid_r(int gid, struct group *grp, 37 char *buf, size_t buflen, struct group **result) { 38 return getgrgid_r(gid, grp, buf, buflen, result); 39 } 40 41 static int mygetgrnam_r(const char *name, struct group *grp, 42 char *buf, size_t buflen, struct group **result) { 43 return getgrnam_r(name, grp, buf, buflen, result); 44 } 45 */ 46 import "C" 47 48 func current() (*User, error) { 49 return lookupUnixUid(syscall.Getuid()) 50 } 51 52 func lookupUser(username string) (*User, error) { 53 var pwd C.struct_passwd 54 var result *C.struct_passwd 55 nameC := make([]byte, len(username)+1) 56 copy(nameC, username) 57 58 buf := alloc(userBuffer) 59 defer buf.free() 60 61 err := retryWithBuffer(buf, func() syscall.Errno { 62 // mygetpwnam_r is a wrapper around getpwnam_r to avoid 63 // passing a size_t to getpwnam_r, because for unknown 64 // reasons passing a size_t to getpwnam_r doesn't work on 65 // Solaris. 66 return syscall.Errno(C.mygetpwnam_r((*C.char)(unsafe.Pointer(&nameC[0])), 67 &pwd, 68 (*C.char)(buf.ptr), 69 C.size_t(buf.size), 70 &result)) 71 }) 72 if err != nil { 73 return nil, fmt.Errorf("user: lookup username %s: %v", username, err) 74 } 75 if result == nil { 76 return nil, UnknownUserError(username) 77 } 78 return buildUser(&pwd), err 79 } 80 81 func lookupUserId(uid string) (*User, error) { 82 i, e := strconv.Atoi(uid) 83 if e != nil { 84 return nil, e 85 } 86 return lookupUnixUid(i) 87 } 88 89 func lookupUnixUid(uid int) (*User, error) { 90 var pwd C.struct_passwd 91 var result *C.struct_passwd 92 93 buf := alloc(userBuffer) 94 defer buf.free() 95 96 err := retryWithBuffer(buf, func() syscall.Errno { 97 // mygetpwuid_r is a wrapper around getpwuid_r to avoid using uid_t 98 // because C.uid_t(uid) for unknown reasons doesn't work on linux. 99 return syscall.Errno(C.mygetpwuid_r(C.int(uid), 100 &pwd, 101 (*C.char)(buf.ptr), 102 C.size_t(buf.size), 103 &result)) 104 }) 105 if err != nil { 106 return nil, fmt.Errorf("user: lookup userid %d: %v", uid, err) 107 } 108 if result == nil { 109 return nil, UnknownUserIdError(uid) 110 } 111 return buildUser(&pwd), nil 112 } 113 114 func buildUser(pwd *C.struct_passwd) *User { 115 u := &User{ 116 Uid: strconv.FormatUint(uint64(pwd.pw_uid), 10), 117 Gid: strconv.FormatUint(uint64(pwd.pw_gid), 10), 118 Username: C.GoString(pwd.pw_name), 119 Name: C.GoString(pwd.pw_gecos), 120 HomeDir: C.GoString(pwd.pw_dir), 121 } 122 // The pw_gecos field isn't quite standardized. Some docs 123 // say: "It is expected to be a comma separated list of 124 // personal data where the first item is the full name of the 125 // user." 126 if i := strings.Index(u.Name, ","); i >= 0 { 127 u.Name = u.Name[:i] 128 } 129 return u 130 } 131 132 func lookupGroup(groupname string) (*Group, error) { 133 var grp C.struct_group 134 var result *C.struct_group 135 136 buf := alloc(groupBuffer) 137 defer buf.free() 138 cname := make([]byte, len(groupname)+1) 139 copy(cname, groupname) 140 141 err := retryWithBuffer(buf, func() syscall.Errno { 142 return syscall.Errno(C.mygetgrnam_r((*C.char)(unsafe.Pointer(&cname[0])), 143 &grp, 144 (*C.char)(buf.ptr), 145 C.size_t(buf.size), 146 &result)) 147 }) 148 if err != nil { 149 return nil, fmt.Errorf("user: lookup groupname %s: %v", groupname, err) 150 } 151 if result == nil { 152 return nil, UnknownGroupError(groupname) 153 } 154 return buildGroup(&grp), nil 155 } 156 157 func lookupGroupId(gid string) (*Group, error) { 158 i, e := strconv.Atoi(gid) 159 if e != nil { 160 return nil, e 161 } 162 return lookupUnixGid(i) 163 } 164 165 func lookupUnixGid(gid int) (*Group, error) { 166 var grp C.struct_group 167 var result *C.struct_group 168 169 buf := alloc(groupBuffer) 170 defer buf.free() 171 172 err := retryWithBuffer(buf, func() syscall.Errno { 173 // mygetgrgid_r is a wrapper around getgrgid_r to avoid using gid_t 174 // because C.gid_t(gid) for unknown reasons doesn't work on linux. 175 return syscall.Errno(C.mygetgrgid_r(C.int(gid), 176 &grp, 177 (*C.char)(buf.ptr), 178 C.size_t(buf.size), 179 &result)) 180 }) 181 if err != nil { 182 return nil, fmt.Errorf("user: lookup groupid %d: %v", gid, err) 183 } 184 if result == nil { 185 return nil, UnknownGroupIdError(strconv.Itoa(gid)) 186 } 187 return buildGroup(&grp), nil 188 } 189 190 func buildGroup(grp *C.struct_group) *Group { 191 g := &Group{ 192 Gid: strconv.Itoa(int(grp.gr_gid)), 193 Name: C.GoString(grp.gr_name), 194 } 195 return g 196 } 197 198 type bufferKind C.int 199 200 const ( 201 userBuffer = bufferKind(C._SC_GETPW_R_SIZE_MAX) 202 groupBuffer = bufferKind(C._SC_GETGR_R_SIZE_MAX) 203 ) 204 205 func (k bufferKind) initialSize() C.size_t { 206 sz := C.sysconf(C.int(k)) 207 if sz == -1 { 208 // DragonFly and FreeBSD do not have _SC_GETPW_R_SIZE_MAX. 209 // Additionally, not all Linux systems have it, either. For 210 // example, the musl libc returns -1. 211 return 1024 212 } 213 if !isSizeReasonable(int64(sz)) { 214 // Truncate. If this truly isn't enough, retryWithBuffer will error on the first run. 215 return maxBufferSize 216 } 217 return C.size_t(sz) 218 } 219 220 type memBuffer struct { 221 ptr unsafe.Pointer 222 size C.size_t 223 } 224 225 func alloc(kind bufferKind) *memBuffer { 226 sz := kind.initialSize() 227 return &memBuffer{ 228 ptr: C.malloc(sz), 229 size: sz, 230 } 231 } 232 233 func (mb *memBuffer) resize(newSize C.size_t) { 234 mb.ptr = C.realloc(mb.ptr, newSize) 235 mb.size = newSize 236 } 237 238 func (mb *memBuffer) free() { 239 C.free(mb.ptr) 240 } 241 242 // retryWithBuffer repeatedly calls f(), increasing the size of the 243 // buffer each time, until f succeeds, fails with a non-ERANGE error, 244 // or the buffer exceeds a reasonable limit. 245 func retryWithBuffer(buf *memBuffer, f func() syscall.Errno) error { 246 for { 247 errno := f() 248 if errno == 0 { 249 return nil 250 } else if errno != syscall.ERANGE { 251 return errno 252 } 253 newSize := buf.size * 2 254 if !isSizeReasonable(int64(newSize)) { 255 return fmt.Errorf("internal buffer exceeds %d bytes", maxBufferSize) 256 } 257 buf.resize(newSize) 258 } 259 } 260 261 const maxBufferSize = 1 << 20 262 263 func isSizeReasonable(sz int64) bool { 264 return sz > 0 && sz <= maxBufferSize 265 } 266 267 // Because we can't use cgo in tests: 268 func structPasswdForNegativeTest() C.struct_passwd { 269 sp := C.struct_passwd{} 270 sp.pw_uid = 1<<32 - 2 271 sp.pw_gid = 1<<32 - 3 272 return sp 273 }