github.com/hikaru7719/go@v0.0.0-20181025140707-c8b2ac68906a/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 // +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 currentGroup() (*Group, error) { 133 return lookupUnixGid(syscall.Getgid()) 134 } 135 136 func lookupGroup(groupname string) (*Group, error) { 137 var grp C.struct_group 138 var result *C.struct_group 139 140 buf := alloc(groupBuffer) 141 defer buf.free() 142 cname := make([]byte, len(groupname)+1) 143 copy(cname, groupname) 144 145 err := retryWithBuffer(buf, func() syscall.Errno { 146 return syscall.Errno(C.mygetgrnam_r((*C.char)(unsafe.Pointer(&cname[0])), 147 &grp, 148 (*C.char)(buf.ptr), 149 C.size_t(buf.size), 150 &result)) 151 }) 152 if err != nil { 153 return nil, fmt.Errorf("user: lookup groupname %s: %v", groupname, err) 154 } 155 if result == nil { 156 return nil, UnknownGroupError(groupname) 157 } 158 return buildGroup(&grp), nil 159 } 160 161 func lookupGroupId(gid string) (*Group, error) { 162 i, e := strconv.Atoi(gid) 163 if e != nil { 164 return nil, e 165 } 166 return lookupUnixGid(i) 167 } 168 169 func lookupUnixGid(gid int) (*Group, error) { 170 var grp C.struct_group 171 var result *C.struct_group 172 173 buf := alloc(groupBuffer) 174 defer buf.free() 175 176 err := retryWithBuffer(buf, func() syscall.Errno { 177 // mygetgrgid_r is a wrapper around getgrgid_r to avoid using gid_t 178 // because C.gid_t(gid) for unknown reasons doesn't work on linux. 179 return syscall.Errno(C.mygetgrgid_r(C.int(gid), 180 &grp, 181 (*C.char)(buf.ptr), 182 C.size_t(buf.size), 183 &result)) 184 }) 185 if err != nil { 186 return nil, fmt.Errorf("user: lookup groupid %d: %v", gid, err) 187 } 188 if result == nil { 189 return nil, UnknownGroupIdError(strconv.Itoa(gid)) 190 } 191 return buildGroup(&grp), nil 192 } 193 194 func buildGroup(grp *C.struct_group) *Group { 195 g := &Group{ 196 Gid: strconv.Itoa(int(grp.gr_gid)), 197 Name: C.GoString(grp.gr_name), 198 } 199 return g 200 } 201 202 type bufferKind C.int 203 204 const ( 205 userBuffer = bufferKind(C._SC_GETPW_R_SIZE_MAX) 206 groupBuffer = bufferKind(C._SC_GETGR_R_SIZE_MAX) 207 ) 208 209 func (k bufferKind) initialSize() C.size_t { 210 sz := C.sysconf(C.int(k)) 211 if sz == -1 { 212 // DragonFly and FreeBSD do not have _SC_GETPW_R_SIZE_MAX. 213 // Additionally, not all Linux systems have it, either. For 214 // example, the musl libc returns -1. 215 return 1024 216 } 217 if !isSizeReasonable(int64(sz)) { 218 // Truncate. If this truly isn't enough, retryWithBuffer will error on the first run. 219 return maxBufferSize 220 } 221 return C.size_t(sz) 222 } 223 224 type memBuffer struct { 225 ptr unsafe.Pointer 226 size C.size_t 227 } 228 229 func alloc(kind bufferKind) *memBuffer { 230 sz := kind.initialSize() 231 return &memBuffer{ 232 ptr: C.malloc(sz), 233 size: sz, 234 } 235 } 236 237 func (mb *memBuffer) resize(newSize C.size_t) { 238 mb.ptr = C.realloc(mb.ptr, newSize) 239 mb.size = newSize 240 } 241 242 func (mb *memBuffer) free() { 243 C.free(mb.ptr) 244 } 245 246 // retryWithBuffer repeatedly calls f(), increasing the size of the 247 // buffer each time, until f succeeds, fails with a non-ERANGE error, 248 // or the buffer exceeds a reasonable limit. 249 func retryWithBuffer(buf *memBuffer, f func() syscall.Errno) error { 250 for { 251 errno := f() 252 if errno == 0 { 253 return nil 254 } else if errno != syscall.ERANGE { 255 return errno 256 } 257 newSize := buf.size * 2 258 if !isSizeReasonable(int64(newSize)) { 259 return fmt.Errorf("internal buffer exceeds %d bytes", maxBufferSize) 260 } 261 buf.resize(newSize) 262 } 263 } 264 265 const maxBufferSize = 1 << 20 266 267 func isSizeReasonable(sz int64) bool { 268 return sz > 0 && sz <= maxBufferSize 269 } 270 271 // Because we can't use cgo in tests: 272 func structPasswdForNegativeTest() C.struct_passwd { 273 sp := C.struct_passwd{} 274 sp.pw_uid = 1<<32 - 2 275 sp.pw_gid = 1<<32 - 3 276 return sp 277 }