1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
16
17 package schema
18
19 import (
20 "bufio"
21 "errors"
22 "os"
23 "os/user"
24 "strconv"
25 "strings"
26 "sync"
27 )
28
29 type intBool struct {
30 int
31 bool
32 }
33
34 var (
35 lookupMu sync.RWMutex
36 uidName = map[int]string{}
37 gidName = map[int]string{}
38 userUid = map[string]intBool{}
39 groupGid = map[string]intBool{}
40
41 parsedGroups, parsedPasswd bool
42 )
43
44 func getUserFromUid(id int) string {
45 return cachedName(id, uidName, lookupUserid)
46 }
47
48 func getGroupFromGid(id int) string {
49 return cachedName(id, gidName, lookupGroupId)
50 }
51
52 func getUidFromName(user string) (int, bool) {
53 return cachedId(user, userUid, lookupUserToId)
54 }
55
56 func getGidFromName(group string) (int, bool) {
57 return cachedId(group, groupGid, lookupGroupToId)
58 }
59
60 func cachedName(id int, m map[int]string, fn func(int) string) string {
61
62
63 lookupMu.RLock()
64 name, ok := m[id]
65 lookupMu.RUnlock()
66 if ok {
67 return name
68 }
69 lookupMu.Lock()
70 defer lookupMu.Unlock()
71 name, ok = m[id]
72 if ok {
73 return name
74 }
75 m[id] = fn(id)
76 return m[id]
77 }
78
79 func cachedId(name string, m map[string]intBool, fn func(string) (int, bool)) (int, bool) {
80
81
82 lookupMu.RLock()
83 intb, ok := m[name]
84 lookupMu.RUnlock()
85 if ok {
86 return intb.int, intb.bool
87 }
88 lookupMu.Lock()
89 defer lookupMu.Unlock()
90 intb, ok = m[name]
91 if ok {
92 return intb.int, intb.bool
93 }
94 id, ok := fn(name)
95 m[name] = intBool{id, ok}
96 return id, ok
97 }
98
99
100 func lookupUserToId(name string) (uid int, ok bool) {
101 u, err := user.Lookup(name)
102 if err == nil {
103 uid, err := strconv.Atoi(u.Uid)
104 if err == nil {
105 return uid, true
106 }
107 }
108 return
109 }
110
111
112 func lookupUserid(id int) string {
113 u, err := user.LookupId(strconv.Itoa(id))
114 if err == nil {
115 return u.Username
116 }
117 var ue user.UnknownUserIdError
118 if errors.As(err, &ue) {
119 return ""
120 }
121 if parsedPasswd {
122 return ""
123 }
124 parsedPasswd = true
125 populateMap(uidName, nil, "/etc/passwd")
126 return uidName[id]
127 }
128
129
130 func lookupGroupToId(group string) (gid int, ok bool) {
131 if !parsedGroups {
132 lookupGroupId(0)
133 }
134 intb := groupGid[group]
135 return intb.int, intb.bool
136 }
137
138
139 func lookupGroupId(id int) string {
140 if parsedGroups {
141 return ""
142 }
143 parsedGroups = true
144 populateMap(gidName, groupGid, "/etc/group")
145 return gidName[id]
146 }
147
148
149
150
151 func populateMap(m map[int]string, nameMap map[string]intBool, file string) {
152 f, err := os.Open(file)
153 if err != nil {
154 return
155 }
156 defer f.Close()
157 bufr := bufio.NewReader(f)
158 for {
159 line, err := bufr.ReadString('\n')
160 if err != nil {
161 return
162 }
163 parts := strings.SplitN(line, ":", 4)
164 if len(parts) >= 3 {
165 idstr := parts[2]
166 id, err := strconv.Atoi(idstr)
167 if err == nil {
168 m[id] = parts[0]
169 if nameMap != nil {
170 nameMap[parts[0]] = intBool{id, true}
171 }
172 }
173 }
174 }
175 }