-
Notifications
You must be signed in to change notification settings - Fork 601
/
Copy pathgroup.go
158 lines (136 loc) · 3.81 KB
/
group.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
// -*- Mode: Go; indent-tabs-mode: t -*-
/*
* Copyright (C) 2017 Canonical Ltd
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package osutil
// #include <stdlib.h>
// #include <sys/types.h>
// #include <grp.h>
// #include <unistd.h>
import "C"
import (
"fmt"
"os/user"
"strconv"
"syscall"
"unsafe"
)
// hrm, user.LookupGroup() doesn't exist yet:
// https://github.com/golang/go/issues/2617
//
// Use implementation from upcoming releases:
// https://golang.org/src/os/user/lookup_unix.go
func lookupGroup(groupname string) (string, error) {
var grp C.struct_group
var result *C.struct_group
buf := alloc(groupBuffer)
defer buf.free()
cname := C.CString(groupname)
defer C.free(unsafe.Pointer(cname))
err := retryWithBuffer(buf, func() syscall.Errno {
return syscall.Errno(C.getgrnam_r(cname,
&grp,
(*C.char)(buf.ptr),
C.size_t(buf.size),
&result))
})
if err != nil {
return "", fmt.Errorf("group: lookup groupname %s: %v", groupname, err)
}
if result == nil {
return "", fmt.Errorf("group: unknown group %s", groupname)
}
return strconv.Itoa(int(grp.gr_gid)), nil
}
type bufferKind C.int
const (
groupBuffer = bufferKind(C._SC_GETGR_R_SIZE_MAX)
)
func (k bufferKind) initialSize() C.size_t {
sz := C.sysconf(C.int(k))
if sz == -1 {
// DragonFly and FreeBSD do not have _SC_GETPW_R_SIZE_MAX.
// Additionally, not all Linux systems have it, either. For
// example, the musl libc returns -1.
return 1024
}
if !isSizeReasonable(int64(sz)) {
// Truncate. If this truly isn't enough, retryWithBuffer will error on the first run.
return maxBufferSize
}
return C.size_t(sz)
}
type memBuffer struct {
ptr unsafe.Pointer
size C.size_t
}
func alloc(kind bufferKind) *memBuffer {
sz := kind.initialSize()
return &memBuffer{
ptr: C.malloc(sz),
size: sz,
}
}
func (mb *memBuffer) resize(newSize C.size_t) {
mb.ptr = C.realloc(mb.ptr, newSize)
mb.size = newSize
}
func (mb *memBuffer) free() {
C.free(mb.ptr)
}
// retryWithBuffer repeatedly calls f(), increasing the size of the
// buffer each time, until f succeeds, fails with a non-ERANGE error,
// or the buffer exceeds a reasonable limit.
func retryWithBuffer(buf *memBuffer, f func() syscall.Errno) error {
for {
errno := f()
if errno == 0 {
return nil
} else if errno != syscall.ERANGE {
return errno
}
newSize := buf.size * 2
if !isSizeReasonable(int64(newSize)) {
return fmt.Errorf("internal buffer exceeds %d bytes", maxBufferSize)
}
buf.resize(newSize)
}
}
const maxBufferSize = 1 << 20
func isSizeReasonable(sz int64) bool {
return sz > 0 && sz <= maxBufferSize
}
// end code from https://golang.org/src/os/user/lookup_unix.go
// FindUid returns the identifier of the given UNIX user name.
func FindUid(username string) (uint64, error) {
user, err := user.Lookup(username)
if err != nil {
return 0, err
}
return strconv.ParseUint(user.Uid, 10, 64)
}
// FindGid returns the identifier of the given UNIX group name.
func FindGid(group string) (uint64, error) {
// In golang 1.8 we can use the built-in function like this:
//group, err := user.LookupGroup(group)
group, err := lookupGroup(group)
if err != nil {
return 0, err
}
// In golang 1.8 we can parse the group.Gid string instead.
//return strconv.ParseUint(group.Gid, 10, 64)
return strconv.ParseUint(group, 10, 64)
}