...
Run Format

Source file src/os/user/lookup_unix.go

     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 darwin dragonfly freebsd !android,linux netbsd openbsd solaris
     6	// +build cgo
     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 := C.CString(username)
    56		defer C.free(unsafe.Pointer(nameC))
    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(nameC,
    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
    98			// to avoid using uid_t because C.uid_t(uid) for
    99			// unknown reasons doesn't work on linux.
   100			return syscall.Errno(C.mygetpwuid_r(C.int(uid),
   101				&pwd,
   102				(*C.char)(buf.ptr),
   103				C.size_t(buf.size),
   104				&result))
   105		})
   106		if err != nil {
   107			return nil, fmt.Errorf("user: lookup userid %d: %v", uid, err)
   108		}
   109		if result == nil {
   110			return nil, UnknownUserIdError(uid)
   111		}
   112		return buildUser(&pwd), nil
   113	}
   114	
   115	func buildUser(pwd *C.struct_passwd) *User {
   116		u := &User{
   117			Uid:      strconv.Itoa(int(pwd.pw_uid)),
   118			Gid:      strconv.Itoa(int(pwd.pw_gid)),
   119			Username: C.GoString(pwd.pw_name),
   120			Name:     C.GoString(pwd.pw_gecos),
   121			HomeDir:  C.GoString(pwd.pw_dir),
   122		}
   123		// The pw_gecos field isn't quite standardized. Some docs
   124		// say: "It is expected to be a comma separated list of
   125		// personal data where the first item is the full name of the
   126		// user."
   127		if i := strings.Index(u.Name, ","); i >= 0 {
   128			u.Name = u.Name[:i]
   129		}
   130		return u
   131	}
   132	
   133	func currentGroup() (*Group, error) {
   134		return lookupUnixGid(syscall.Getgid())
   135	}
   136	
   137	func lookupGroup(groupname string) (*Group, error) {
   138		var grp C.struct_group
   139		var result *C.struct_group
   140	
   141		buf := alloc(groupBuffer)
   142		defer buf.free()
   143		cname := C.CString(groupname)
   144		defer C.free(unsafe.Pointer(cname))
   145	
   146		err := retryWithBuffer(buf, func() syscall.Errno {
   147			return syscall.Errno(C.mygetgrnam_r(cname,
   148				&grp,
   149				(*C.char)(buf.ptr),
   150				C.size_t(buf.size),
   151				&result))
   152		})
   153		if err != nil {
   154			return nil, fmt.Errorf("user: lookup groupname %s: %v", groupname, err)
   155		}
   156		if result == nil {
   157			return nil, UnknownGroupError(groupname)
   158		}
   159		return buildGroup(&grp), nil
   160	}
   161	
   162	func lookupGroupId(gid string) (*Group, error) {
   163		i, e := strconv.Atoi(gid)
   164		if e != nil {
   165			return nil, e
   166		}
   167		return lookupUnixGid(i)
   168	}
   169	
   170	func lookupUnixGid(gid int) (*Group, error) {
   171		var grp C.struct_group
   172		var result *C.struct_group
   173	
   174		buf := alloc(groupBuffer)
   175		defer buf.free()
   176	
   177		err := retryWithBuffer(buf, func() syscall.Errno {
   178			// mygetgrgid_r is a wrapper around getgrgid_r to
   179			// to avoid using gid_t because C.gid_t(gid) for
   180			// unknown reasons doesn't work on linux.
   181			return syscall.Errno(C.mygetgrgid_r(C.int(gid),
   182				&grp,
   183				(*C.char)(buf.ptr),
   184				C.size_t(buf.size),
   185				&result))
   186		})
   187		if err != nil {
   188			return nil, fmt.Errorf("user: lookup groupid %d: %v", gid, err)
   189		}
   190		if result == nil {
   191			return nil, UnknownGroupIdError(strconv.Itoa(gid))
   192		}
   193		return buildGroup(&grp), nil
   194	}
   195	
   196	func buildGroup(grp *C.struct_group) *Group {
   197		g := &Group{
   198			Gid:  strconv.Itoa(int(grp.gr_gid)),
   199			Name: C.GoString(grp.gr_name),
   200		}
   201		return g
   202	}
   203	
   204	type bufferKind C.int
   205	
   206	const (
   207		userBuffer  = bufferKind(C._SC_GETPW_R_SIZE_MAX)
   208		groupBuffer = bufferKind(C._SC_GETGR_R_SIZE_MAX)
   209	)
   210	
   211	func (k bufferKind) initialSize() C.size_t {
   212		sz := C.sysconf(C.int(k))
   213		if sz == -1 {
   214			// DragonFly and FreeBSD do not have _SC_GETPW_R_SIZE_MAX.
   215			// Additionally, not all Linux systems have it, either. For
   216			// example, the musl libc returns -1.
   217			return 1024
   218		}
   219		if !isSizeReasonable(int64(sz)) {
   220			// Truncate.  If this truly isn't enough, retryWithBuffer will error on the first run.
   221			return maxBufferSize
   222		}
   223		return C.size_t(sz)
   224	}
   225	
   226	type memBuffer struct {
   227		ptr  unsafe.Pointer
   228		size C.size_t
   229	}
   230	
   231	func alloc(kind bufferKind) *memBuffer {
   232		sz := kind.initialSize()
   233		return &memBuffer{
   234			ptr:  C.malloc(sz),
   235			size: sz,
   236		}
   237	}
   238	
   239	func (mb *memBuffer) resize(newSize C.size_t) {
   240		mb.ptr = C.realloc(mb.ptr, newSize)
   241		mb.size = newSize
   242	}
   243	
   244	func (mb *memBuffer) free() {
   245		C.free(mb.ptr)
   246	}
   247	
   248	// retryWithBuffer repeatedly calls f(), increasing the size of the
   249	// buffer each time, until f succeeds, fails with a non-ERANGE error,
   250	// or the buffer exceeds a reasonable limit.
   251	func retryWithBuffer(buf *memBuffer, f func() syscall.Errno) error {
   252		for {
   253			errno := f()
   254			if errno == 0 {
   255				return nil
   256			} else if errno != syscall.ERANGE {
   257				return errno
   258			}
   259			newSize := buf.size * 2
   260			if !isSizeReasonable(int64(newSize)) {
   261				return fmt.Errorf("internal buffer exceeds %d bytes", maxBufferSize)
   262			}
   263			buf.resize(newSize)
   264		}
   265	}
   266	
   267	const maxBufferSize = 1 << 20
   268	
   269	func isSizeReasonable(sz int64) bool {
   270		return sz > 0 && sz <= maxBufferSize
   271	}
   272	

View as plain text