github.com/hanwen/go-fuse@v1.0.0/unionfs/cachingfs.go (about) 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 5 package unionfs 6 7 import ( 8 "fmt" 9 "log" 10 "strings" 11 "time" 12 13 "github.com/hanwen/go-fuse/fuse" 14 "github.com/hanwen/go-fuse/fuse/nodefs" 15 "github.com/hanwen/go-fuse/fuse/pathfs" 16 ) 17 18 const _XATTRSEP = "@XATTR@" 19 20 type attrResponse struct { 21 *fuse.Attr 22 fuse.Status 23 } 24 25 type xattrResponse struct { 26 data []byte 27 fuse.Status 28 } 29 30 type dirResponse struct { 31 entries []fuse.DirEntry 32 fuse.Status 33 } 34 35 type linkResponse struct { 36 linkContent string 37 fuse.Status 38 } 39 40 // Caches filesystem metadata. 41 type cachingFileSystem struct { 42 pathfs.FileSystem 43 44 attributes *TimedCache 45 dirs *TimedCache 46 links *TimedCache 47 xattr *TimedCache 48 } 49 50 func 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 61 func 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 69 func 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 78 func 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 86 func 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 108 func (fs *cachingFileSystem) DropCache() { 109 for _, c := range []*TimedCache{fs.attributes, fs.dirs, fs.links, fs.xattr} { 110 c.DropAll(nil) 111 } 112 } 113 114 func (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 125 func (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 131 func (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 136 func (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 141 func (fs *cachingFileSystem) String() string { 142 return fmt.Sprintf("cachingFileSystem(%v)", fs.FileSystem) 143 } 144 145 func (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 }