github.com/mtsmfm/go/src@v0.0.0-20221020090648-44bdcb9f8fde/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 unix && !android && cgo && !osusergo 6 7 package user 8 9 import ( 10 "fmt" 11 "strconv" 12 "strings" 13 "syscall" 14 "unsafe" 15 ) 16 17 /* 18 #cgo solaris CFLAGS: -D_POSIX_PTHREAD_SEMANTICS 19 #include <unistd.h> 20 #include <sys/types.h> 21 #include <pwd.h> 22 #include <grp.h> 23 #include <stdlib.h> 24 25 static int mygetpwuid_r(int uid, struct passwd *pwd, 26 char *buf, size_t buflen, struct passwd **result) { 27 return getpwuid_r(uid, pwd, buf, buflen, result); 28 } 29 30 static int mygetpwnam_r(const char *name, struct passwd *pwd, 31 char *buf, size_t buflen, struct passwd **result) { 32 return getpwnam_r(name, pwd, buf, buflen, result); 33 } 34 35 static int mygetgrgid_r(int gid, struct group *grp, 36 char *buf, size_t buflen, struct group **result) { 37 return getgrgid_r(gid, grp, buf, buflen, result); 38 } 39 40 static int mygetgrnam_r(const char *name, struct group *grp, 41 char *buf, size_t buflen, struct group **result) { 42 return getgrnam_r(name, grp, buf, buflen, result); 43 } 44 */ 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 // mygetpwnam_r is a wrapper around getpwnam_r to avoid 62 // passing a size_t to getpwnam_r, because for unknown 63 // reasons passing a size_t to getpwnam_r doesn't work on 64 // Solaris. 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 // mygetpwuid_r is a wrapper around getpwuid_r to avoid using uid_t 97 // because C.uid_t(uid) for unknown reasons doesn't work on linux. 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 // The pw_gecos field isn't quite standardized. Some docs 122 // say: "It is expected to be a comma separated list of 123 // personal data where the first item is the full name of the 124 // user." 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 // mygetgrgid_r is a wrapper around getgrgid_r to avoid using gid_t 171 // because C.gid_t(gid) for unknown reasons doesn't work on linux. 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 // DragonFly and FreeBSD do not have _SC_GETPW_R_SIZE_MAX. 206 // Additionally, not all Linux systems have it, either. For 207 // example, the musl libc returns -1. 208 return 1024 209 } 210 if !isSizeReasonable(int64(sz)) { 211 // Truncate. If this truly isn't enough, retryWithBuffer will error on the first run. 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 // retryWithBuffer repeatedly calls f(), increasing the size of the 240 // buffer each time, until f succeeds, fails with a non-ERANGE error, 241 // or the buffer exceeds a reasonable limit. 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 // Because we can't use cgo in tests: 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 }