syncthing/lib/fs/platform_common.go

93 lines
2.4 KiB
Go
Raw Normal View History

// Copyright (C) 2022 The Syncthing Authors.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at https://mozilla.org/MPL/2.0/.
package fs
import (
"strconv"
2022-08-12 05:48:00 +00:00
"sync"
"time"
"github.com/syncthing/syncthing/lib/protocol"
)
// unixPlatformData is used on all platforms, because apart from being the
// implementation for BasicFilesystem on Unixes it's also the implementation
// in fakeFS.
2022-08-12 05:48:00 +00:00
func unixPlatformData(fs Filesystem, name string, userCache *userCache, groupCache *groupCache) (protocol.PlatformData, error) {
stat, err := fs.Lstat(name)
if err != nil {
return protocol.PlatformData{}, err
}
ownerUID := stat.Owner()
ownerName := ""
2022-08-12 05:48:00 +00:00
if user := userCache.lookup(strconv.Itoa(ownerUID)); user != nil {
ownerName = user.Username
} else if ownerUID == 0 {
// We couldn't look up a name, but UID zero should be "root". This
// fixup works around the (unlikely) situation where the ownership
// is 0:0 but we can't look up a name for either uid zero or gid
// zero. If that were the case we'd return a zero PlatformData which
// wouldn't get serialized over the wire and the other side would
// assume a lack of ownership info...
ownerName = "root"
}
groupID := stat.Group()
groupName := ""
2022-08-12 05:48:00 +00:00
if group := groupCache.lookup(strconv.Itoa(ownerUID)); group != nil {
groupName = group.Name
} else if groupID == 0 {
groupName = "root"
}
return protocol.PlatformData{
Unix: &protocol.UnixData{
OwnerName: ownerName,
GroupName: groupName,
UID: ownerUID,
GID: groupID,
},
}, nil
}
2022-08-12 05:48:00 +00:00
type valueCache[K comparable, V any] struct {
validity time.Duration
fill func(K) (V, error)
mut sync.Mutex
cache map[K]cacheEntry[V]
}
type cacheEntry[V any] struct {
value V
when time.Time
}
func newValueCache[K comparable, V any](validity time.Duration, fill func(K) (V, error)) *valueCache[K, V] {
return &valueCache[K, V]{
validity: validity,
fill: fill,
cache: make(map[K]cacheEntry[V]),
}
}
func (c *valueCache[K, V]) lookup(key K) V {
c.mut.Lock()
defer c.mut.Unlock()
if e, ok := c.cache[key]; ok && time.Since(e.when) < c.validity {
return e.value
}
var e cacheEntry[V]
if val, err := c.fill(key); err == nil {
e.value = val
}
e.when = time.Now()
c.cache[key] = e
return e.value
}