use cachingfs

This commit is contained in:
Barak Michener 2018-03-31 17:23:40 -07:00
parent 892908fcf5
commit 61b665af13
10 changed files with 1963 additions and 2 deletions

5
Gopkg.lock generated
View file

@ -14,7 +14,8 @@
"fuse",
"fuse/nodefs",
"fuse/pathfs",
"splice"
"splice",
"unionfs"
]
revision = "a9ddcb8a4b609500fc59c89ccc9ee05f00a5fefd"
@ -60,6 +61,6 @@
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
inputs-digest = "9bee1a4379bcd4b9176976a3cedac4a5c846c42ebdc54c623d3fb551c6921aa9"
inputs-digest = "3446597fa9ac971d950ce0953fbc4e46796f213d08a7781d54fe17858e2af33b"
solver-name = "gps-cdcl"
solver-version = 1

View file

@ -6,6 +6,7 @@ import (
"github.com/hanwen/go-fuse/fuse"
"github.com/hanwen/go-fuse/fuse/nodefs"
"github.com/hanwen/go-fuse/fuse/pathfs"
"github.com/hanwen/go-fuse/unionfs"
"github.com/sirupsen/logrus"
)
@ -31,6 +32,7 @@ func NewWsFs(opts WsFsOpts, req chan RequestCallback, closer chan bool) *pathfs.
if opts.ReadOnly {
fs = pathfs.NewReadonlyFileSystem(fs)
}
fs = unionfs.NewCachingFileSystem(fs, 1*time.Second)
return pathfs.NewPathNodeFs(fs, nil)
}

452
vendor/github.com/hanwen/go-fuse/unionfs/autounion.go generated vendored Normal file
View file

@ -0,0 +1,452 @@
// Copyright 2016 the Go-FUSE Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package unionfs
import (
"fmt"
"io/ioutil"
"log"
"os"
"path/filepath"
"strings"
"sync"
"syscall"
"time"
"github.com/hanwen/go-fuse/fuse"
"github.com/hanwen/go-fuse/fuse/nodefs"
"github.com/hanwen/go-fuse/fuse/pathfs"
)
type knownFs struct {
unionFS pathfs.FileSystem
nodeFS *pathfs.PathNodeFs
}
// Creates unions for all files under a given directory,
// walking the tree and looking for directories D which have a
// D/READONLY symlink.
//
// A union for A/B/C will placed under directory A-B-C.
type autoUnionFs struct {
pathfs.FileSystem
debug bool
lock sync.RWMutex
zombies map[string]bool
knownFileSystems map[string]knownFs
nameRootMap map[string]string
root string
nodeFs *pathfs.PathNodeFs
options *AutoUnionFsOptions
}
type AutoUnionFsOptions struct {
UnionFsOptions
nodefs.Options
pathfs.PathNodeFsOptions
// If set, run updateKnownFses() after mounting.
UpdateOnMount bool
// If set hides the _READONLY file.
HideReadonly bool
// Expose this version in /status/gounionfs_version
Version string
}
const (
_READONLY = "READONLY"
_STATUS = "status"
_CONFIG = "config"
_DEBUG = "debug"
_ROOT = "root"
_VERSION = "gounionfs_version"
_SCAN_CONFIG = ".scan_config"
)
func NewAutoUnionFs(directory string, options AutoUnionFsOptions) pathfs.FileSystem {
if options.HideReadonly {
options.HiddenFiles = append(options.HiddenFiles, _READONLY)
}
a := &autoUnionFs{
knownFileSystems: make(map[string]knownFs),
nameRootMap: make(map[string]string),
zombies: make(map[string]bool),
options: &options,
FileSystem: pathfs.NewDefaultFileSystem(),
}
directory, err := filepath.Abs(directory)
if err != nil {
panic("filepath.Abs returned err")
}
a.root = directory
return a
}
func (fs *autoUnionFs) String() string {
return fmt.Sprintf("autoUnionFs(%s)", fs.root)
}
func (fs *autoUnionFs) OnMount(nodeFs *pathfs.PathNodeFs) {
fs.nodeFs = nodeFs
if fs.options.UpdateOnMount {
time.AfterFunc(100*time.Millisecond, func() { fs.updateKnownFses() })
}
}
func (fs *autoUnionFs) addAutomaticFs(roots []string) {
relative := strings.TrimLeft(strings.Replace(roots[0], fs.root, "", -1), "/")
name := strings.Replace(relative, "/", "-", -1)
if fs.getUnionFs(name) == nil {
fs.addFs(name, roots)
}
}
func (fs *autoUnionFs) createFs(name string, roots []string) fuse.Status {
fs.lock.Lock()
defer fs.lock.Unlock()
if fs.zombies[name] {
log.Printf("filesystem named %q is being removed", name)
return fuse.EBUSY
}
for workspace, root := range fs.nameRootMap {
if root == roots[0] && workspace != name {
log.Printf("Already have a union FS for directory %s in workspace %s",
roots[0], workspace)
return fuse.EBUSY
}
}
known := fs.knownFileSystems[name]
if known.unionFS != nil {
log.Println("Already have a workspace:", name)
return fuse.EBUSY
}
ufs, err := NewUnionFsFromRoots(roots, &fs.options.UnionFsOptions, true)
if err != nil {
log.Println("Could not create UnionFs:", err)
return fuse.EPERM
}
log.Printf("Adding workspace %v for roots %v", name, ufs.String())
nfs := pathfs.NewPathNodeFs(ufs, &fs.options.PathNodeFsOptions)
code := fs.nodeFs.Mount(name, nfs.Root(), &fs.options.Options)
if code.Ok() {
fs.knownFileSystems[name] = knownFs{
ufs,
nfs,
}
fs.nameRootMap[name] = roots[0]
}
return code
}
func (fs *autoUnionFs) rmFs(name string) (code fuse.Status) {
fs.lock.Lock()
defer fs.lock.Unlock()
if fs.zombies[name] {
return fuse.ENOENT
}
known := fs.knownFileSystems[name]
if known.unionFS == nil {
return fuse.ENOENT
}
root := fs.nameRootMap[name]
delete(fs.knownFileSystems, name)
delete(fs.nameRootMap, name)
fs.zombies[name] = true
fs.lock.Unlock()
code = fs.nodeFs.Unmount(name)
fs.lock.Lock()
delete(fs.zombies, name)
if !code.Ok() {
// Reinstate.
log.Printf("Unmount failed for %s. Code %v", name, code)
fs.knownFileSystems[name] = known
fs.nameRootMap[name] = root
}
return code
}
func (fs *autoUnionFs) addFs(name string, roots []string) (code fuse.Status) {
if name == _CONFIG || name == _STATUS || name == _SCAN_CONFIG {
return fuse.EINVAL
}
return fs.createFs(name, roots)
}
func (fs *autoUnionFs) getRoots(path string) []string {
ro := filepath.Join(path, _READONLY)
fi, err := os.Lstat(ro)
fiDir, errDir := os.Stat(ro)
if err != nil || errDir != nil {
return nil
}
if fi.Mode()&os.ModeSymlink != 0 && fiDir.IsDir() {
// TODO - should recurse and chain all READONLYs
// together.
return []string{path, ro}
}
return nil
}
func (fs *autoUnionFs) visit(path string, fi os.FileInfo, err error) error {
if fi != nil && fi.IsDir() {
roots := fs.getRoots(path)
if roots != nil {
fs.addAutomaticFs(roots)
}
}
return nil
}
func (fs *autoUnionFs) updateKnownFses() {
// We unroll the first level of entries in the root manually in order
// to allow symbolic links on that level.
directoryEntries, err := ioutil.ReadDir(fs.root)
if err == nil {
for _, dir := range directoryEntries {
if dir.IsDir() || dir.Mode()&os.ModeSymlink != 0 {
path := filepath.Join(fs.root, dir.Name())
dir, _ = os.Stat(path)
fs.visit(path, dir, nil)
filepath.Walk(path,
func(path string, fi os.FileInfo, err error) error {
return fs.visit(path, fi, err)
})
}
}
}
}
func (fs *autoUnionFs) Readlink(path string, context *fuse.Context) (out string, code fuse.Status) {
comps := strings.Split(path, string(filepath.Separator))
if comps[0] == _STATUS && comps[1] == _ROOT {
return fs.root, fuse.OK
}
if comps[0] != _CONFIG {
return "", fuse.ENOENT
}
name := comps[1]
fs.lock.RLock()
defer fs.lock.RUnlock()
root, ok := fs.nameRootMap[name]
if ok {
return root, fuse.OK
}
return "", fuse.ENOENT
}
func (fs *autoUnionFs) getUnionFs(name string) pathfs.FileSystem {
fs.lock.RLock()
defer fs.lock.RUnlock()
return fs.knownFileSystems[name].unionFS
}
func (fs *autoUnionFs) Symlink(pointedTo string, linkName string, context *fuse.Context) (code fuse.Status) {
comps := strings.Split(linkName, "/")
if len(comps) != 2 {
return fuse.EPERM
}
if comps[0] == _CONFIG {
roots := fs.getRoots(pointedTo)
if roots == nil {
return fuse.Status(syscall.ENOTDIR)
}
name := comps[1]
return fs.addFs(name, roots)
}
return fuse.EPERM
}
func (fs *autoUnionFs) Unlink(path string, context *fuse.Context) (code fuse.Status) {
comps := strings.Split(path, "/")
if len(comps) != 2 {
return fuse.EPERM
}
if comps[0] == _CONFIG && comps[1] != _SCAN_CONFIG {
code = fs.rmFs(comps[1])
} else {
code = fuse.ENOENT
}
return code
}
// Must define this, because ENOSYS will suspend all GetXAttr calls.
func (fs *autoUnionFs) GetXAttr(name string, attr string, context *fuse.Context) ([]byte, fuse.Status) {
return nil, fuse.ENOATTR
}
func (fs *autoUnionFs) GetAttr(path string, context *fuse.Context) (*fuse.Attr, fuse.Status) {
a := &fuse.Attr{
Owner: *fuse.CurrentOwner(),
}
if path == "" || path == _CONFIG || path == _STATUS {
a.Mode = fuse.S_IFDIR | 0755
return a, fuse.OK
}
if path == filepath.Join(_STATUS, _VERSION) {
a.Mode = fuse.S_IFREG | 0644
a.Size = uint64(len(fs.options.Version))
return a, fuse.OK
}
if path == filepath.Join(_STATUS, _DEBUG) {
a.Mode = fuse.S_IFREG | 0644
a.Size = uint64(len(fs.DebugData()))
return a, fuse.OK
}
if path == filepath.Join(_STATUS, _ROOT) {
a.Mode = syscall.S_IFLNK | 0644
return a, fuse.OK
}
if path == filepath.Join(_CONFIG, _SCAN_CONFIG) {
a.Mode = fuse.S_IFREG | 0644
return a, fuse.OK
}
comps := strings.Split(path, string(filepath.Separator))
if len(comps) > 1 && comps[0] == _CONFIG {
fs := fs.getUnionFs(comps[1])
if fs == nil {
return nil, fuse.ENOENT
}
a.Mode = syscall.S_IFLNK | 0644
return a, fuse.OK
}
return nil, fuse.ENOENT
}
func (fs *autoUnionFs) StatusDir() (stream []fuse.DirEntry, status fuse.Status) {
stream = make([]fuse.DirEntry, 0, 10)
stream = []fuse.DirEntry{
{Name: _VERSION, Mode: fuse.S_IFREG | 0644},
{Name: _DEBUG, Mode: fuse.S_IFREG | 0644},
{Name: _ROOT, Mode: syscall.S_IFLNK | 0644},
}
return stream, fuse.OK
}
func (fs *autoUnionFs) DebugData() string {
conn := fs.nodeFs.Connector()
if conn.Server() == nil {
return "autoUnionFs.mountState not set"
}
setting := conn.Server().KernelSettings()
msg := fmt.Sprintf(
"Version: %v\n"+
"Bufferpool: %v\n"+
"Kernel: %v\n",
fs.options.Version,
conn.Server().DebugData(),
&setting)
if conn != nil {
msg += fmt.Sprintf("Live inodes: %d\n", conn.InodeHandleCount())
}
return msg
}
func (fs *autoUnionFs) Open(path string, flags uint32, context *fuse.Context) (nodefs.File, fuse.Status) {
if path == filepath.Join(_STATUS, _DEBUG) {
if flags&fuse.O_ANYWRITE != 0 {
return nil, fuse.EPERM
}
return nodefs.NewDataFile([]byte(fs.DebugData())), fuse.OK
}
if path == filepath.Join(_STATUS, _VERSION) {
if flags&fuse.O_ANYWRITE != 0 {
return nil, fuse.EPERM
}
return nodefs.NewDataFile([]byte(fs.options.Version)), fuse.OK
}
if path == filepath.Join(_CONFIG, _SCAN_CONFIG) {
if flags&fuse.O_ANYWRITE != 0 {
fs.updateKnownFses()
}
return nodefs.NewDevNullFile(), fuse.OK
}
return nil, fuse.ENOENT
}
func (fs *autoUnionFs) Truncate(name string, offset uint64, context *fuse.Context) (code fuse.Status) {
if name != filepath.Join(_CONFIG, _SCAN_CONFIG) {
log.Println("Huh? Truncating unsupported write file", name)
return fuse.EPERM
}
return fuse.OK
}
func (fs *autoUnionFs) OpenDir(name string, context *fuse.Context) (stream []fuse.DirEntry, status fuse.Status) {
switch name {
case _STATUS:
return fs.StatusDir()
case _CONFIG:
case "/":
name = ""
case "":
default:
log.Printf("Argh! Don't know how to list dir %v", name)
return nil, fuse.ENOSYS
}
fs.lock.RLock()
defer fs.lock.RUnlock()
stream = make([]fuse.DirEntry, 0, len(fs.knownFileSystems)+5)
if name == _CONFIG {
for k := range fs.knownFileSystems {
stream = append(stream, fuse.DirEntry{
Name: k,
Mode: syscall.S_IFLNK | 0644,
})
}
}
if name == "" {
stream = append(stream, fuse.DirEntry{
Name: _CONFIG,
Mode: uint32(fuse.S_IFDIR | 0755),
},
fuse.DirEntry{
Name: _STATUS,
Mode: uint32(fuse.S_IFDIR | 0755),
})
}
return stream, status
}
func (fs *autoUnionFs) StatFs(name string) *fuse.StatfsOut {
return &fuse.StatfsOut{}
}

151
vendor/github.com/hanwen/go-fuse/unionfs/cachingfs.go generated vendored Normal file
View file

@ -0,0 +1,151 @@
// Copyright 2016 the Go-FUSE Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package unionfs
import (
"fmt"
"log"
"strings"
"time"
"github.com/hanwen/go-fuse/fuse"
"github.com/hanwen/go-fuse/fuse/nodefs"
"github.com/hanwen/go-fuse/fuse/pathfs"
)
const _XATTRSEP = "@XATTR@"
type attrResponse struct {
*fuse.Attr
fuse.Status
}
type xattrResponse struct {
data []byte
fuse.Status
}
type dirResponse struct {
entries []fuse.DirEntry
fuse.Status
}
type linkResponse struct {
linkContent string
fuse.Status
}
// Caches filesystem metadata.
type cachingFileSystem struct {
pathfs.FileSystem
attributes *TimedCache
dirs *TimedCache
links *TimedCache
xattr *TimedCache
}
func readDir(fs pathfs.FileSystem, name string) *dirResponse {
origStream, code := fs.OpenDir(name, nil)
r := &dirResponse{nil, code}
if !code.Ok() {
return r
}
r.entries = origStream
return r
}
func getAttr(fs pathfs.FileSystem, name string) *attrResponse {
a, code := fs.GetAttr(name, nil)
return &attrResponse{
Attr: a,
Status: code,
}
}
func getXAttr(fs pathfs.FileSystem, nameAttr string) *xattrResponse {
ns := strings.SplitN(nameAttr, _XATTRSEP, 2)
a, code := fs.GetXAttr(ns[0], ns[1], nil)
return &xattrResponse{
data: a,
Status: code,
}
}
func readLink(fs pathfs.FileSystem, name string) *linkResponse {
a, code := fs.Readlink(name, nil)
return &linkResponse{
linkContent: a,
Status: code,
}
}
func NewCachingFileSystem(fs pathfs.FileSystem, ttl time.Duration) pathfs.FileSystem {
c := new(cachingFileSystem)
c.FileSystem = fs
c.attributes = NewTimedCache(func(n string) (interface{}, bool) {
a := getAttr(fs, n)
return a, a.Ok()
}, ttl)
c.dirs = NewTimedCache(func(n string) (interface{}, bool) {
d := readDir(fs, n)
return d, d.Ok()
}, ttl)
c.links = NewTimedCache(func(n string) (interface{}, bool) {
l := readLink(fs, n)
return l, l.Ok()
}, ttl)
c.xattr = NewTimedCache(func(n string) (interface{}, bool) {
l := getXAttr(fs, n)
return l, l.Ok()
}, ttl)
return c
}
func (fs *cachingFileSystem) DropCache() {
for _, c := range []*TimedCache{fs.attributes, fs.dirs, fs.links, fs.xattr} {
c.DropAll(nil)
}
}
func (fs *cachingFileSystem) GetAttr(name string, context *fuse.Context) (*fuse.Attr, fuse.Status) {
if name == _DROP_CACHE {
return &fuse.Attr{
Mode: fuse.S_IFREG | 0777,
}, fuse.OK
}
r := fs.attributes.Get(name).(*attrResponse)
return r.Attr, r.Status
}
func (fs *cachingFileSystem) GetXAttr(name string, attr string, context *fuse.Context) ([]byte, fuse.Status) {
key := name + _XATTRSEP + attr
r := fs.xattr.Get(key).(*xattrResponse)
return r.data, r.Status
}
func (fs *cachingFileSystem) Readlink(name string, context *fuse.Context) (string, fuse.Status) {
r := fs.links.Get(name).(*linkResponse)
return r.linkContent, r.Status
}
func (fs *cachingFileSystem) OpenDir(name string, context *fuse.Context) (stream []fuse.DirEntry, status fuse.Status) {
r := fs.dirs.Get(name).(*dirResponse)
return r.entries, r.Status
}
func (fs *cachingFileSystem) String() string {
return fmt.Sprintf("cachingFileSystem(%v)", fs.FileSystem)
}
func (fs *cachingFileSystem) Open(name string, flags uint32, context *fuse.Context) (f nodefs.File, status fuse.Status) {
if flags&fuse.O_ANYWRITE != 0 && name == _DROP_CACHE {
log.Println("Dropping cache for", fs)
fs.DropCache()
}
return fs.FileSystem.Open(name, flags, context)
}

36
vendor/github.com/hanwen/go-fuse/unionfs/create.go generated vendored Normal file
View file

@ -0,0 +1,36 @@
// Copyright 2016 the Go-FUSE Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package unionfs
import (
"os"
"github.com/hanwen/go-fuse/fuse/pathfs"
)
func NewUnionFsFromRoots(roots []string, opts *UnionFsOptions, roCaching bool) (pathfs.FileSystem, error) {
fses := make([]pathfs.FileSystem, 0)
for i, r := range roots {
var fs pathfs.FileSystem
fi, err := os.Stat(r)
if err != nil {
return nil, err
}
if fi.IsDir() {
fs = pathfs.NewLoopbackFileSystem(r)
}
if fs == nil {
return nil, err
}
if i > 0 && roCaching {
fs = NewCachingFileSystem(fs, 0)
}
fses = append(fses, fs)
}
return NewUnionFs(fses, *opts)
}

125
vendor/github.com/hanwen/go-fuse/unionfs/dircache.go generated vendored Normal file
View file

@ -0,0 +1,125 @@
// Copyright 2016 the Go-FUSE Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package unionfs
import (
"sync"
"time"
"github.com/hanwen/go-fuse/fuse"
"github.com/hanwen/go-fuse/fuse/pathfs"
)
// newDirnameMap reads the contents of the given directory. On error,
// returns a nil map. This forces reloads in the dirCache until we
// succeed.
func newDirnameMap(fs pathfs.FileSystem, dir string) map[string]bool {
stream, code := fs.OpenDir(dir, nil)
if code == fuse.ENOENT {
// The directory not existing is not an error.
return map[string]bool{}
}
if !code.Ok() {
return nil
}
result := make(map[string]bool)
for _, e := range stream {
if e.Mode&fuse.S_IFREG != 0 {
result[e.Name] = true
}
}
return result
}
// dirCache caches names in a directory for some time.
//
// If called when the cache is expired, the filenames are read afresh in
// the background.
type dirCache struct {
dir string
ttl time.Duration
fs pathfs.FileSystem
// Protects data below.
lock sync.RWMutex
// If nil, you may call refresh() to schedule a new one.
names map[string]bool
updateRunning bool
}
func (c *dirCache) setMap(newMap map[string]bool) {
c.lock.Lock()
defer c.lock.Unlock()
c.names = newMap
c.updateRunning = false
_ = time.AfterFunc(c.ttl,
func() { c.DropCache() })
}
func (c *dirCache) DropCache() {
c.lock.Lock()
defer c.lock.Unlock()
c.names = nil
}
// Try to refresh: if another update is already running, do nothing,
// otherwise, read the directory and set it.
func (c *dirCache) maybeRefresh() {
c.lock.Lock()
defer c.lock.Unlock()
if c.updateRunning {
return
}
c.updateRunning = true
go func() {
newmap := newDirnameMap(c.fs, c.dir)
c.setMap(newmap)
}()
}
func (c *dirCache) RemoveEntry(name string) {
c.lock.Lock()
defer c.lock.Unlock()
if c.names == nil {
go c.maybeRefresh()
return
}
delete(c.names, name)
}
func (c *dirCache) AddEntry(name string) {
c.lock.Lock()
defer c.lock.Unlock()
if c.names == nil {
go c.maybeRefresh()
return
}
c.names[name] = true
}
func newDirCache(fs pathfs.FileSystem, dir string, ttl time.Duration) *dirCache {
dc := new(dirCache)
dc.dir = dir
dc.fs = fs
dc.ttl = ttl
return dc
}
func (c *dirCache) HasEntry(name string) (mapPresent bool, found bool) {
c.lock.RLock()
defer c.lock.RUnlock()
if c.names == nil {
go c.maybeRefresh()
return false, false
}
return true, c.names[name]
}

111
vendor/github.com/hanwen/go-fuse/unionfs/timedcache.go generated vendored Normal file
View file

@ -0,0 +1,111 @@
// Copyright 2016 the Go-FUSE Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package unionfs
import (
"sync"
"time"
)
type cacheEntry struct {
data interface{}
// expiry is the absolute timestamp of the expiry.
expiry time.Time
}
// TimedIntCache caches the result of fetch() for some time. It is
// thread-safe. Calls of fetch() do no happen inside a critical
// section, so when multiple concurrent Get()s happen for the same
// key, multiple fetch() calls may be issued for the same key.
type TimedCacheFetcher func(name string) (value interface{}, cacheable bool)
type TimedCache struct {
fetch TimedCacheFetcher
// ttl is the duration of the cache.
ttl time.Duration
cacheMapMutex sync.RWMutex
cacheMap map[string]*cacheEntry
PurgeTimer *time.Timer
}
// Creates a new cache with the given TTL. If TTL <= 0, the caching is
// indefinite.
func NewTimedCache(fetcher TimedCacheFetcher, ttl time.Duration) *TimedCache {
l := new(TimedCache)
l.ttl = ttl
l.fetch = fetcher
l.cacheMap = make(map[string]*cacheEntry)
return l
}
func (c *TimedCache) Get(name string) interface{} {
c.cacheMapMutex.RLock()
info, ok := c.cacheMap[name]
c.cacheMapMutex.RUnlock()
valid := ok && (c.ttl <= 0 || info.expiry.After(time.Now()))
if valid {
return info.data
}
return c.GetFresh(name)
}
func (c *TimedCache) Set(name string, val interface{}) {
c.cacheMapMutex.Lock()
defer c.cacheMapMutex.Unlock()
c.cacheMap[name] = &cacheEntry{
data: val,
expiry: time.Now().Add(c.ttl),
}
}
func (c *TimedCache) DropEntry(name string) {
c.cacheMapMutex.Lock()
defer c.cacheMapMutex.Unlock()
delete(c.cacheMap, name)
}
func (c *TimedCache) GetFresh(name string) interface{} {
data, ok := c.fetch(name)
if ok {
c.Set(name, data)
}
return data
}
// Drop all expired entries.
func (c *TimedCache) Purge() {
keys := make([]string, 0, len(c.cacheMap))
now := time.Now()
c.cacheMapMutex.Lock()
defer c.cacheMapMutex.Unlock()
for k, v := range c.cacheMap {
if now.After(v.expiry) {
keys = append(keys, k)
}
}
for _, k := range keys {
delete(c.cacheMap, k)
}
}
func (c *TimedCache) DropAll(names []string) {
c.cacheMapMutex.Lock()
defer c.cacheMapMutex.Unlock()
if names == nil {
c.cacheMap = make(map[string]*cacheEntry, len(c.cacheMap))
} else {
for _, nm := range names {
delete(c.cacheMap, nm)
}
}
}

1032
vendor/github.com/hanwen/go-fuse/unionfs/unionfs.go generated vendored Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,37 @@
// Copyright 2016 the Go-FUSE Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package unionfs
import (
"syscall"
"unsafe"
)
// Darwin doesn't have support for syscall.Getxattr() so we pull it into its own file and implement it by hand on Darwin.
func Getxattr(path string, attr string, dest []byte) (sz int, err error) {
var _p0 *byte
_p0, err = syscall.BytePtrFromString(path)
if err != nil {
return
}
var _p1 *byte
_p1, err = syscall.BytePtrFromString(attr)
if err != nil {
return
}
var _p2 unsafe.Pointer
if len(dest) > 0 {
_p2 = unsafe.Pointer(&dest[0])
} else {
var _zero uintptr
_p2 = unsafe.Pointer(&_zero)
}
r0, _, e1 := syscall.Syscall6(syscall.SYS_GETXATTR, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), uintptr(_p2), uintptr(len(dest)), 0, 0)
sz = int(r0)
if e1 != 0 {
err = e1
}
return
}

View file

@ -0,0 +1,14 @@
// Copyright 2016 the Go-FUSE Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package unionfs
import (
"syscall"
)
// Darwin doesn't have support for syscall.Getxattr() so we pull it into its own file and implement it by hand on Darwin.
func Getxattr(path string, attr string, dest []byte) (sz int, err error) {
return syscall.Getxattr(path, attr, dest)
}