1// Copyright 2016 the Go-FUSE Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style 3// license that can be found in the LICENSE file. 4 5package unionfs 6 7import ( 8 "fmt" 9 "log" 10 "strings" 11 "time" 12 13 "github.com/hanwen/go-fuse/v2/fuse" 14 "github.com/hanwen/go-fuse/v2/fuse/nodefs" 15 "github.com/hanwen/go-fuse/v2/fuse/pathfs" 16) 17 18const _XATTRSEP = "@XATTR@" 19 20type attrResponse struct { 21 *fuse.Attr 22 fuse.Status 23} 24 25type xattrResponse struct { 26 data []byte 27 fuse.Status 28} 29 30type dirResponse struct { 31 entries []fuse.DirEntry 32 fuse.Status 33} 34 35type linkResponse struct { 36 linkContent string 37 fuse.Status 38} 39 40// Caches filesystem metadata. 41type cachingFileSystem struct { 42 pathfs.FileSystem 43 44 attributes *TimedCache 45 dirs *TimedCache 46 links *TimedCache 47 xattr *TimedCache 48} 49 50func readDir(fs pathfs.FileSystem, name string) *dirResponse { 51 origStream, code := fs.OpenDir(name, nil) 52 53 r := &dirResponse{nil, code} 54 if !code.Ok() { 55 return r 56 } 57 r.entries = origStream 58 return r 59} 60 61func getAttr(fs pathfs.FileSystem, name string) *attrResponse { 62 a, code := fs.GetAttr(name, nil) 63 return &attrResponse{ 64 Attr: a, 65 Status: code, 66 } 67} 68 69func getXAttr(fs pathfs.FileSystem, nameAttr string) *xattrResponse { 70 ns := strings.SplitN(nameAttr, _XATTRSEP, 2) 71 a, code := fs.GetXAttr(ns[0], ns[1], nil) 72 return &xattrResponse{ 73 data: a, 74 Status: code, 75 } 76} 77 78func readLink(fs pathfs.FileSystem, name string) *linkResponse { 79 a, code := fs.Readlink(name, nil) 80 return &linkResponse{ 81 linkContent: a, 82 Status: code, 83 } 84} 85 86func NewCachingFileSystem(fs pathfs.FileSystem, ttl time.Duration) pathfs.FileSystem { 87 c := new(cachingFileSystem) 88 c.FileSystem = fs 89 c.attributes = NewTimedCache(func(n string) (interface{}, bool) { 90 a := getAttr(fs, n) 91 return a, a.Ok() 92 }, ttl) 93 c.dirs = NewTimedCache(func(n string) (interface{}, bool) { 94 d := readDir(fs, n) 95 return d, d.Ok() 96 }, ttl) 97 c.links = NewTimedCache(func(n string) (interface{}, bool) { 98 l := readLink(fs, n) 99 return l, l.Ok() 100 }, ttl) 101 c.xattr = NewTimedCache(func(n string) (interface{}, bool) { 102 l := getXAttr(fs, n) 103 return l, l.Ok() 104 }, ttl) 105 return c 106} 107 108func (fs *cachingFileSystem) DropCache() { 109 for _, c := range []*TimedCache{fs.attributes, fs.dirs, fs.links, fs.xattr} { 110 c.DropAll(nil) 111 } 112} 113 114func (fs *cachingFileSystem) GetAttr(name string, context *fuse.Context) (*fuse.Attr, fuse.Status) { 115 if name == _DROP_CACHE { 116 return &fuse.Attr{ 117 Mode: fuse.S_IFREG | 0777, 118 }, fuse.OK 119 } 120 121 r := fs.attributes.Get(name).(*attrResponse) 122 return r.Attr, r.Status 123} 124 125func (fs *cachingFileSystem) GetXAttr(name string, attr string, context *fuse.Context) ([]byte, fuse.Status) { 126 key := name + _XATTRSEP + attr 127 r := fs.xattr.Get(key).(*xattrResponse) 128 return r.data, r.Status 129} 130 131func (fs *cachingFileSystem) Readlink(name string, context *fuse.Context) (string, fuse.Status) { 132 r := fs.links.Get(name).(*linkResponse) 133 return r.linkContent, r.Status 134} 135 136func (fs *cachingFileSystem) OpenDir(name string, context *fuse.Context) (stream []fuse.DirEntry, status fuse.Status) { 137 r := fs.dirs.Get(name).(*dirResponse) 138 return r.entries, r.Status 139} 140 141func (fs *cachingFileSystem) String() string { 142 return fmt.Sprintf("cachingFileSystem(%v)", fs.FileSystem) 143} 144 145func (fs *cachingFileSystem) Open(name string, flags uint32, context *fuse.Context) (f nodefs.File, status fuse.Status) { 146 if flags&fuse.O_ANYWRITE != 0 && name == _DROP_CACHE { 147 log.Println("Dropping cache for", fs) 148 fs.DropCache() 149 } 150 return fs.FileSystem.Open(name, flags, context) 151} 152