125 lines
2.5 KiB
Go
125 lines
2.5 KiB
Go
// 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]
|
|
}
|