151 lines
3.5 KiB
Go
151 lines
3.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 (
|
|
"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)
|
|
}
|