package plg_backend_nfs import ( "bufio" "os" "strconv" "strings" . "github.com/mickael-kerjean/filestash/server/common" ) var ( cacheForEtc AppCache cacheForGroup AppCache ) const ( DEFAULT_UID = 1000 DEFAULT_GID = 1000 ) func init() { cacheForEtc = NewAppCache(120, 60) cacheForGroup = NewAppCache(120, 60) } func extractUserInfo(uidHint string, gidHint string, gidsHint string) (uint32, uint32, []uint32) { // case 1: everything is being sent as "uid=number, gid=number and gids=number,number,number" if _uid, err := strconv.Atoi(uidHint); err == nil { var ( uid uint32 = uint32(_uid) gid uint32 gids []uint32 ) if _gid, err := strconv.Atoi(gidHint); err == nil { gid = uint32(_gid) } else { gid = uid } for _, t := range strings.Split(gidsHint, ",") { if gid, err := strconv.Atoi(strings.TrimSpace(t)); err == nil { gids = append(gids, uint32(gid)) } } return uid, gid, gids } // case 2: auto detect everything, aka "uid=www-data gid=www-data gids=..." based on uid=www-data if _uid, _gid, err := extractFromEtcPasswd(uidHint); err == nil { return _uid, _gid, extractFromEtcGroup(uidHint, _gid) } // case 3: base case return DEFAULT_UID, DEFAULT_GID, []uint32{} } func extractFromEtcPasswd(username string) (uint32, uint32, error) { if v := cacheForEtc.Get(map[string]string{"username": username}); v != nil { inCache := v.([]int) return uint32(inCache[0]), uint32(inCache[1]), nil } f, err := os.OpenFile("/etc/passwd", os.O_RDONLY, os.ModePerm) if err != nil { return DEFAULT_UID, DEFAULT_GID, err } defer f.Close() lines := bufio.NewReader(f) for { line, _, err := lines.ReadLine() if err != nil { break } s := strings.Split(string(line), ":") if len(s) != 7 { continue } else if username != s[0] { continue } u, err := strconv.Atoi(s[2]) if err != nil { continue } g, err := strconv.Atoi(s[3]) if err != nil { continue } cacheForEtc.Set(map[string]string{"username": username}, []int{u, g}) return uint32(u), uint32(g), nil } return DEFAULT_UID, DEFAULT_GID, ErrNotFound } func extractFromEtcGroup(username string, primary uint32) []uint32 { if v := cacheForGroup.Get(map[string]string{"username": username}); v != nil { return v.([]uint32) } f, err := os.OpenFile("/etc/group", os.O_RDONLY, os.ModePerm) if err != nil { return []uint32{} } defer f.Close() gids := []uint32{} lines := bufio.NewReader(f) for { line, _, err := lines.ReadLine() if err != nil { break } s := strings.Split(string(line), ":") if len(s) != 4 { continue } else if username != s[3] { continue } if gid, err := strconv.Atoi(s[2]); err == nil { ugid := uint32(gid) if ugid != primary { gids = append(gids, ugid) } } cacheForGroup.Set(map[string]string{"username": username}, gids) } return gids }