github.com/slspeek/camlistore_namedsearch@v0.0.0-20140519202248-ed6f70f7721a/pkg/schema/lookup.go (about) 1 /* 2 Copyright 2012 Google Inc. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 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 // guards rest 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 // TODO: use singleflight library here, keyed by 'id', rather than this lookupMu lock, 61 // which is too coarse. 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 // lost race, already populated 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 // TODO: use singleflight library here, keyed by 'name', rather than this lookupMu lock, 80 // which is too coarse. 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 // lost race, already populated 92 } 93 id, ok := fn(name) 94 m[name] = intBool{id, ok} 95 return id, ok 96 } 97 98 func lookupUserToId(name string) (uid int, ok bool) { 99 u, err := user.Lookup(name) 100 if err == nil { 101 uid, err := strconv.Atoi(u.Uid) 102 if err == nil { 103 return uid, true 104 } 105 } 106 return 107 } 108 109 func lookupGroupToId(group string) (gid int, ok bool) { 110 if !parsedGroups { 111 lookupGroupId(0) // force them to be loaded 112 } 113 intb := groupGid[group] 114 return intb.int, intb.bool 115 } 116 117 // lookupMu is held 118 func lookupGroupId(id int) string { 119 if parsedGroups { 120 return "" 121 } 122 parsedGroups = true 123 populateMap(gidName, groupGid, "/etc/group") 124 return gidName[id] 125 } 126 127 // lookupMu is held 128 func lookupUserid(id int) string { 129 u, err := user.LookupId(strconv.Itoa(id)) 130 if err == nil { 131 return u.Username 132 } 133 if _, ok := err.(user.UnknownUserIdError); ok { 134 return "" 135 } 136 if parsedPasswd { 137 return "" 138 } 139 parsedPasswd = true 140 populateMap(uidName, nil, "/etc/passwd") 141 return uidName[id] 142 } 143 144 // Lame fallback parsing /etc/password for non-cgo systems where os/user doesn't work, 145 // and used for groups (which also happens to work on OS X, generally) 146 // nameMap may be nil. 147 func populateMap(m map[int]string, nameMap map[string]intBool, file string) { 148 f, err := os.Open(file) 149 if err != nil { 150 return 151 } 152 defer f.Close() 153 bufr := bufio.NewReader(f) 154 for { 155 line, err := bufr.ReadString('\n') 156 if err != nil { 157 return 158 } 159 parts := strings.SplitN(line, ":", 4) 160 if len(parts) >= 3 { 161 idstr := parts[2] 162 id, err := strconv.Atoi(idstr) 163 if err == nil { 164 m[id] = parts[0] 165 if nameMap != nil { 166 nameMap[parts[0]] = intBool{id, true} 167 } 168 } 169 } 170 } 171 }