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