Home Download Docs Code Community
     1	/*
     2	Copyright 2012 The Perkeep Authors
     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		"strconv"
    23		"strings"
    24		"sync"
    25	)
    26	
    27	type intBool struct {
    28		int
    29		bool
    30	}
    31	
    32	var (
    33		lookupMu sync.RWMutex // guards rest
    34		uidName  = map[int]string{}
    35		gidName  = map[int]string{}
    36		userUid  = map[string]intBool{}
    37		groupGid = map[string]intBool{}
    38	
    39		parsedGroups, parsedPasswd bool
    40	)
    41	
    42	func getUserFromUid(id int) string {
    43		return cachedName(id, uidName, lookupUserid)
    44	}
    45	
    46	func getGroupFromGid(id int) string {
    47		return cachedName(id, gidName, lookupGroupId)
    48	}
    49	
    50	func getUidFromName(user string) (int, bool) {
    51		return cachedId(user, userUid, lookupUserToId)
    52	}
    53	
    54	func getGidFromName(group string) (int, bool) {
    55		return cachedId(group, groupGid, lookupGroupToId)
    56	}
    57	
    58	func cachedName(id int, m map[int]string, fn func(int) string) string {
    59		// TODO: use singleflight library here, keyed by 'id', rather than this lookupMu lock,
    60		// which is too coarse.
    61		lookupMu.RLock()
    62		name, ok := m[id]
    63		lookupMu.RUnlock()
    64		if ok {
    65			return name
    66		}
    67		lookupMu.Lock()
    68		defer lookupMu.Unlock()
    69		name, ok = m[id]
    70		if ok {
    71			return name // lost race, already populated
    72		}
    73		m[id] = fn(id)
    74		return m[id]
    75	}
    76	
    77	func cachedId(name string, m map[string]intBool, fn func(string) (int, bool)) (int, bool) {
    78		// TODO: use singleflight library here, keyed by 'name', rather than this lookupMu lock,
    79		// which is too coarse.
    80		lookupMu.RLock()
    81		intb, ok := m[name]
    82		lookupMu.RUnlock()
    83		if ok {
    84			return intb.int, intb.bool
    85		}
    86		lookupMu.Lock()
    87		defer lookupMu.Unlock()
    88		intb, ok = m[name]
    89		if ok {
    90			return intb.int, intb.bool // lost race, already populated
    91		}
    92		id, ok := fn(name)
    93		m[name] = intBool{id, ok}
    94		return id, ok
    95	}
    96	
    97	func lookupGroupToId(group string) (gid int, ok bool) {
    98		if !parsedGroups {
    99			lookupGroupId(0) // force them to be loaded
   100		}
   101		intb := groupGid[group]
   102		return intb.int, intb.bool
   103	}
   104	
   105	// lookupMu is held
   106	func lookupGroupId(id int) string {
   107		if parsedGroups {
   108			return ""
   109		}
   110		parsedGroups = true
   111		populateMap(gidName, groupGid, "/etc/group")
   112		return gidName[id]
   113	}
   114	
   115	// Lame fallback parsing /etc/password for non-cgo systems where os/user doesn't work,
   116	// and used for groups (which also happens to work on OS X, generally)
   117	// nameMap may be nil.
   118	func populateMap(m map[int]string, nameMap map[string]intBool, file string) {
   119		f, err := os.Open(file)
   120		if err != nil {
   121			return
   122		}
   123		defer f.Close()
   124		bufr := bufio.NewReader(f)
   125		for {
   126			line, err := bufr.ReadString('\n')
   127			if err != nil {
   128				return
   129			}
   130			parts := strings.SplitN(line, ":", 4)
   131			if len(parts) >= 3 {
   132				idstr := parts[2]
   133				id, err := strconv.Atoi(idstr)
   134				if err == nil {
   135					m[id] = parts[0]
   136					if nameMap != nil {
   137						nameMap[parts[0]] = intBool{id, true}
   138					}
   139				}
   140			}
   141		}
   142	}
Website layout inspired by memcached.
Content by the authors.