github.com/lovishpuri/go-40569/src@v0.0.0-20230519171745-f8623e7c56cf/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 (cgo || darwin) && !osusergo && unix && !android 6 7 package user 8 9 import ( 10 "fmt" 11 "runtime" 12 "strconv" 13 "strings" 14 "syscall" 15 "unsafe" 16 ) 17 18 func current() (*User, error) { 19 return lookupUnixUid(syscall.Getuid()) 20 } 21 22 func lookupUser(username string) (*User, error) { 23 var pwd _C_struct_passwd 24 var found bool 25 nameC := make([]byte, len(username)+1) 26 copy(nameC, username) 27 28 err := retryWithBuffer(userBuffer, func(buf []byte) syscall.Errno { 29 var errno syscall.Errno 30 pwd, found, errno = _C_getpwnam_r((*_C_char)(unsafe.Pointer(&nameC[0])), 31 (*_C_char)(unsafe.Pointer(&buf[0])), _C_size_t(len(buf))) 32 return errno 33 }) 34 if err != nil { 35 return nil, fmt.Errorf("user: lookup username %s: %v", username, err) 36 } 37 if !found { 38 return nil, UnknownUserError(username) 39 } 40 return buildUser(&pwd), err 41 } 42 43 func lookupUserId(uid string) (*User, error) { 44 i, e := strconv.Atoi(uid) 45 if e != nil { 46 return nil, e 47 } 48 return lookupUnixUid(i) 49 } 50 51 func lookupUnixUid(uid int) (*User, error) { 52 var pwd _C_struct_passwd 53 var found bool 54 55 err := retryWithBuffer(userBuffer, func(buf []byte) syscall.Errno { 56 var errno syscall.Errno 57 pwd, found, errno = _C_getpwuid_r(_C_uid_t(uid), 58 (*_C_char)(unsafe.Pointer(&buf[0])), _C_size_t(len(buf))) 59 return errno 60 }) 61 if err != nil { 62 return nil, fmt.Errorf("user: lookup userid %d: %v", uid, err) 63 } 64 if !found { 65 return nil, UnknownUserIdError(uid) 66 } 67 return buildUser(&pwd), nil 68 } 69 70 func buildUser(pwd *_C_struct_passwd) *User { 71 u := &User{ 72 Uid: strconv.FormatUint(uint64(_C_pw_uid(pwd)), 10), 73 Gid: strconv.FormatUint(uint64(_C_pw_gid(pwd)), 10), 74 Username: _C_GoString(_C_pw_name(pwd)), 75 Name: _C_GoString(_C_pw_gecos(pwd)), 76 HomeDir: _C_GoString(_C_pw_dir(pwd)), 77 } 78 // The pw_gecos field isn't quite standardized. Some docs 79 // say: "It is expected to be a comma separated list of 80 // personal data where the first item is the full name of the 81 // user." 82 u.Name, _, _ = strings.Cut(u.Name, ",") 83 return u 84 } 85 86 func lookupGroup(groupname string) (*Group, error) { 87 var grp _C_struct_group 88 var found bool 89 90 cname := make([]byte, len(groupname)+1) 91 copy(cname, groupname) 92 93 err := retryWithBuffer(groupBuffer, func(buf []byte) syscall.Errno { 94 var errno syscall.Errno 95 grp, found, errno = _C_getgrnam_r((*_C_char)(unsafe.Pointer(&cname[0])), 96 (*_C_char)(unsafe.Pointer(&buf[0])), _C_size_t(len(buf))) 97 return errno 98 }) 99 if err != nil { 100 return nil, fmt.Errorf("user: lookup groupname %s: %v", groupname, err) 101 } 102 if !found { 103 return nil, UnknownGroupError(groupname) 104 } 105 return buildGroup(&grp), nil 106 } 107 108 func lookupGroupId(gid string) (*Group, error) { 109 i, e := strconv.Atoi(gid) 110 if e != nil { 111 return nil, e 112 } 113 return lookupUnixGid(i) 114 } 115 116 func lookupUnixGid(gid int) (*Group, error) { 117 var grp _C_struct_group 118 var found bool 119 120 err := retryWithBuffer(groupBuffer, func(buf []byte) syscall.Errno { 121 var errno syscall.Errno 122 grp, found, errno = _C_getgrgid_r(_C_gid_t(gid), 123 (*_C_char)(unsafe.Pointer(&buf[0])), _C_size_t(len(buf))) 124 return syscall.Errno(errno) 125 }) 126 if err != nil { 127 return nil, fmt.Errorf("user: lookup groupid %d: %v", gid, err) 128 } 129 if !found { 130 return nil, UnknownGroupIdError(strconv.Itoa(gid)) 131 } 132 return buildGroup(&grp), nil 133 } 134 135 func buildGroup(grp *_C_struct_group) *Group { 136 g := &Group{ 137 Gid: strconv.Itoa(int(_C_gr_gid(grp))), 138 Name: _C_GoString(_C_gr_name(grp)), 139 } 140 return g 141 } 142 143 type bufferKind _C_int 144 145 var ( 146 userBuffer = bufferKind(_C__SC_GETPW_R_SIZE_MAX) 147 groupBuffer = bufferKind(_C__SC_GETGR_R_SIZE_MAX) 148 ) 149 150 func (k bufferKind) initialSize() _C_size_t { 151 sz := _C_sysconf(_C_int(k)) 152 if sz == -1 { 153 // DragonFly and FreeBSD do not have _SC_GETPW_R_SIZE_MAX. 154 // Additionally, not all Linux systems have it, either. For 155 // example, the musl libc returns -1. 156 return 1024 157 } 158 if !isSizeReasonable(int64(sz)) { 159 // Truncate. If this truly isn't enough, retryWithBuffer will error on the first run. 160 return maxBufferSize 161 } 162 return _C_size_t(sz) 163 } 164 165 // retryWithBuffer repeatedly calls f(), increasing the size of the 166 // buffer each time, until f succeeds, fails with a non-ERANGE error, 167 // or the buffer exceeds a reasonable limit. 168 func retryWithBuffer(startSize bufferKind, f func([]byte) syscall.Errno) error { 169 buf := make([]byte, startSize) 170 for { 171 errno := f(buf) 172 if errno == 0 { 173 return nil 174 } else if runtime.GOOS == "aix" && errno+1 == 0 { 175 // On AIX getpwuid_r appears to return -1, 176 // not ERANGE, on buffer overflow. 177 } else if errno != syscall.ERANGE { 178 return errno 179 } 180 newSize := len(buf) * 2 181 if !isSizeReasonable(int64(newSize)) { 182 return fmt.Errorf("internal buffer exceeds %d bytes", maxBufferSize) 183 } 184 buf = make([]byte, newSize) 185 } 186 } 187 188 const maxBufferSize = 1 << 20 189 190 func isSizeReasonable(sz int64) bool { 191 return sz > 0 && sz <= maxBufferSize 192 } 193 194 // Because we can't use cgo in tests: 195 func structPasswdForNegativeTest() _C_struct_passwd { 196 sp := _C_struct_passwd{} 197 *_C_pw_uidp(&sp) = 1<<32 - 2 198 *_C_pw_gidp(&sp) = 1<<32 - 3 199 return sp 200 }